Merge remote-tracking branch 'origin/master' into custom-highlight-color-tabs

This commit is contained in:
Rasmus Karlsson 2024-08-31 12:52:20 +02:00
commit 439d21d009
No known key found for this signature in database
1043 changed files with 76764 additions and 27618 deletions

View file

@ -2,13 +2,28 @@
set -e
# Print all commands as they are run
set -x
if [ ! -f ./bin/chatterino ] || [ ! -x ./bin/chatterino ]; then
echo "ERROR: No chatterino binary file found. This script must be run in the build folder, and chatterino must be built first."
exit 1
fi
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/opt/qt512/lib/"
export PATH="/opt/qt512/bin:$PATH"
if [ -n "$Qt5_DIR" ]; then
echo "Using Qt DIR from Qt5_DIR: $Qt5_DIR"
_QT_DIR="$Qt5_DIR"
elif [ -n "$Qt6_DIR" ]; then
echo "Using Qt DIR from Qt6_DIR: $Qt6_DIR"
_QT_DIR="$Qt6_DIR"
fi
if [ -n "$_QT_DIR" ]; then
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${_QT_DIR}/lib"
export PATH="${_QT_DIR}/bin:$PATH"
else
echo "No Qt environment variable set, assuming system-installed Qt"
fi
script_path=$(readlink -f "$0")
script_dir=$(dirname "$script_path")
@ -25,20 +40,32 @@ echo ""
cp "$chatterino_dir"/resources/icon.png ./appdir/chatterino.png
linuxdeployqt_path="linuxdeployqt-6-x86_64.AppImage"
linuxdeployqt_url="https://github.com/probonopd/linuxdeployqt/releases/download/6/linuxdeployqt-6-x86_64.AppImage"
linuxdeployqt_path="linuxdeployqt-x86_64.AppImage"
linuxdeployqt_url="https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage"
if [ ! -f "$linuxdeployqt_path" ]; then
wget -nv "$linuxdeployqt_url"
echo "Downloading LinuxDeployQT from $linuxdeployqt_url to $linuxdeployqt_path"
curl --location --fail --silent "$linuxdeployqt_url" -o "$linuxdeployqt_path"
chmod a+x "$linuxdeployqt_path"
fi
if [ ! -f appimagetool-x86_64.AppImage ]; then
wget -nv "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage"
chmod a+x appimagetool-x86_64.AppImage
appimagetool_path="appimagetool-x86_64.AppImage"
appimagetool_url="https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage"
if [ ! -f "$appimagetool_path" ]; then
echo "Downloading AppImageTool from $appimagetool_url to $appimagetool_path"
curl --location --fail --silent "$appimagetool_url" -o "$appimagetool_path"
chmod a+x "$appimagetool_path"
fi
# For some reason, the copyright file for libc was not found. We need to manually copy it from the host system
mkdir -p appdir/usr/share/doc/libc6/
cp /usr/share/doc/libc6/copyright appdir/usr/share/doc/libc6/
echo "Run LinuxDeployQT"
./"$linuxdeployqt_path" \
appdir/usr/share/applications/*.desktop \
--appimage-extract-and-run \
appdir/usr/share/applications/com.chatterino.chatterino.desktop \
-no-translations \
-bundle-non-qt-libs \
-unsupported-allow-new-glibc
@ -56,4 +83,9 @@ cd "$here/usr"
exec "$here/usr/bin/chatterino" "$@"' > appdir/AppRun
chmod a+x appdir/AppRun
./appimagetool-x86_64.AppImage appdir
./"$appimagetool_path" \
--appimage-extract-and-run \
appdir
# TODO: Create appimage in a unique directory instead maybe idk?
rm -rf appdir

View file

@ -1,18 +1,38 @@
#!/bin/sh
#!/usr/bin/env bash
if [ -d bin/chatterino.app ] && [ ! -d chatterino.app ]; then
>&2 echo "Moving bin/chatterino.app down one directory"
mv bin/chatterino.app chatterino.app
set -eo pipefail
if [ ! -d chatterino.app ]; then
echo "ERROR: No 'chatterino.app' dir found in the build directory. Make sure you've run ./CI/MacDeploy.sh"
exit 1
fi
if [ -z "$OUTPUT_DMG_PATH" ]; then
echo "ERROR: Must specify the path for where to save the final .dmg. Make sure you've set the OUTPUT_DMG_PATH environment variable."
exit 1
fi
if [ -z "$SKIP_VENV" ]; then
echo "Creating python3 virtual environment"
python3 -m venv venv
echo "Entering python3 virtual environment"
. venv/bin/activate
echo "Installing dmgbuild"
python3 -m pip install dmgbuild
fi
if [ -n "$MACOS_CODESIGN_CERTIFICATE" ]; then
echo "Codesigning force deep inside the app"
codesign -s "$MACOS_CODESIGN_CERTIFICATE" --deep --force chatterino.app
echo "Done!"
fi
echo "Running MACDEPLOYQT"
$Qt5_DIR/bin/macdeployqt chatterino.app
echo "Creating python3 virtual environment"
python3 -m venv venv
echo "Entering python3 virtual environment"
. venv/bin/activate
echo "Installing dmgbuild"
python3 -m pip install dmgbuild
echo "Running dmgbuild.."
dmgbuild --settings ./../.CI/dmg-settings.py -D app=./chatterino.app Chatterino2 chatterino-osx.dmg
dmgbuild --settings ./../.CI/dmg-settings.py -D app=./chatterino.app Chatterino2 "$OUTPUT_DMG_PATH"
echo "Done!"
if [ -n "$MACOS_CODESIGN_CERTIFICATE" ]; then
echo "Codesigning the dmg"
codesign -s "$MACOS_CODESIGN_CERTIFICATE" --deep --force "$OUTPUT_DMG_PATH"
echo "Done!"
fi

View file

@ -1,35 +1,101 @@
#!/bin/sh
set -e
breakline() {
printf "================================================================================\n\n"
}
# Configured in the CI step
install_prefix="appdir/usr"
# The directory we finally pack into our .deb package
packaging_dir="package"
# Get the Ubuntu Release (e.g. 20.04, 22.04 or 24.04)
ubuntu_release="$(lsb_release -rs)"
# The final path where we'll save the .deb package
deb_path="Chatterino-ubuntu-${ubuntu_release}-x86_64.deb"
# Refactor opportunity:
case "$ubuntu_release" in
20.04)
# Qt6 static-linked deb, see https://github.com/Chatterino/docker
dependencies="libc6, libstdc++6, libblkid1, libbsd0, 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, 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)
# 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, libffi8, libmount1, libselinux1, libpcre3, libbsd0, libblkid1, libpcre2-8-0, libmd0"
;;
24.04)
# 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, libicu74, libpcre2-16-0, libssl3, libgraphite2-3, libexpat1, libuuid1, libxcb-util1, libxau6, libxdmcp6, libffi8, libmount1, libselinux1, libpcre3, libbsd0, libblkid1, libpcre2-8-0, libmd0"
;;
*)
echo "Unsupported Ubuntu release $ubuntu_release"
exit 1
;;
esac
echo "Building Ubuntu .deb file on '$ubuntu_release'"
echo "Dependencies: $dependencies"
if [ ! -f ./bin/chatterino ] || [ ! -x ./bin/chatterino ]; then
echo "ERROR: No chatterino binary file found. This script must be run in the build folder, and chatterino must be built first."
exit 1
fi
chatterino_version=$(git describe | cut -c 2-)
echo "Found Chatterino version $chatterino_version via git"
chatterino_version=$(git describe 2>/dev/null) || true
if [ "$(echo "$chatterino_version" | cut -c1-1)" = 'v' ]; then
chatterino_version="$(echo "$chatterino_version" | cut -c2-)"
else
chatterino_version="0.0.0-dev"
fi
rm -vrf "./package" || true # delete any old packaging dir
# Make sure no old remnants of a previous packaging remains
rm -vrf "$packaging_dir"
# create ./package/ from scratch
mkdir package/DEBIAN -p
packaging_dir="$(realpath ./package)"
mkdir -p "$packaging_dir/DEBIAN"
echo "Making control file"
cat >> "$packaging_dir/DEBIAN/control" << EOF
Package: chatterino
Section: net
Priority: optional
Version: $chatterino_version
Architecture: amd64
Maintainer: Mm2PL <mm2pl@kotmisia.pl>
Description: Testing out chatterino as a Ubuntu package
Depends: libc6, libqt5concurrent5, libqt5core5a, libqt5dbus5, libqt5gui5, libqt5multimedia5, libqt5network5, libqt5svg5, libqt5widgets5, libssl1.1, libstdc++6
Depends: $dependencies
Section: net
Priority: optional
Homepage: https://github.com/Chatterino/chatterino2
Description: Ubuntu package built for $ubuntu_release
EOF
echo "Version: $chatterino_version" >> "$packaging_dir/DEBIAN/control"
cat "$packaging_dir/DEBIAN/control"
breakline
echo "Running make install in package dir"
DESTDIR="$packaging_dir" make INSTALL_ROOT="$packaging_dir" -j"$(nproc)" install; find "$packaging_dir/"
echo ""
echo "Building package..."
dpkg-deb --build "$packaging_dir" "Chatterino.deb"
echo "Running make install"
make install
find "$install_prefix"
breakline
echo "Merge install into packaging dir"
cp -rv "$install_prefix/" "$packaging_dir/"
find "$packaging_dir"
breakline
echo "Building package"
dpkg-deb --build "$packaging_dir" "$deb_path"
breakline
echo "Package info"
dpkg --info "$deb_path"
breakline
echo "Package contents"
dpkg --contents "$deb_path"
breakline

39
.CI/MacDeploy.sh Executable file
View file

@ -0,0 +1,39 @@
#!/usr/bin/env bash
# Bundle relevant qt & system dependencies into the ./chatterino.app folder
set -eo pipefail
if [ -d bin/chatterino.app ] && [ ! -d chatterino.app ]; then
>&2 echo "Moving bin/chatterino.app down one directory"
mv bin/chatterino.app chatterino.app
fi
if [ -n "$Qt5_DIR" ]; then
echo "Using Qt DIR from Qt5_DIR: $Qt5_DIR"
_QT_DIR="$Qt5_DIR"
elif [ -n "$Qt6_DIR" ]; then
echo "Using Qt DIR from Qt6_DIR: $Qt6_DIR"
_QT_DIR="$Qt6_DIR"
fi
if [ -n "$_QT_DIR" ]; then
export PATH="${_QT_DIR}/bin:$PATH"
else
echo "No Qt environment variable set, assuming system-installed Qt"
fi
echo "Running MACDEPLOYQT"
_macdeployqt_args=()
if [ -n "$MACOS_CODESIGN_CERTIFICATE" ]; then
_macdeployqt_args+=("-codesign=$MACOS_CODESIGN_CERTIFICATE")
fi
macdeployqt chatterino.app "${_macdeployqt_args[@]}"
if [ -n "$MACOS_CODESIGN_CERTIFICATE" ]; then
# Validate that chatterino.app was codesigned correctly
codesign -v chatterino.app
fi

51
.CI/build-installer.ps1 Normal file
View file

@ -0,0 +1,51 @@
if (-not (Test-Path -PathType Container Chatterino2)) {
Write-Error "Couldn't find a folder called 'Chatterino2' in the current directory.";
exit 1
}
# Check if we're on a tag
$OldErrorActionPref = $ErrorActionPreference;
$ErrorActionPreference = 'Continue';
git describe --exact-match --match 'v*' *> $null;
$isTagged = $?;
$ErrorActionPreference = $OldErrorActionPref;
$defines = $null;
if ($isTagged) {
# This is a release.
# Make sure, any existing `modes` file is overwritten for the user,
# for example when updating from nightly to stable.
Write-Output "" | Out-File Chatterino2/modes -Encoding ASCII;
$installerBaseName = "Chatterino.Installer";
}
else {
Write-Output nightly | Out-File Chatterino2/modes -Encoding ASCII;
$defines = "/DIS_NIGHTLY=1";
$installerBaseName = "Chatterino.Nightly.Installer";
}
if ($Env:GITHUB_OUTPUT) {
# This is used in CI when creating the artifact
"C2_INSTALLER_BASE_NAME=$installerBaseName" >> "$Env:GITHUB_OUTPUT"
}
# Copy vc_redist.x64.exe
if ($null -eq $Env:VCToolsRedistDir) {
Write-Error "VCToolsRedistDir is not set. Forgot to set Visual Studio environment variables?";
exit 1
}
Copy-Item "$Env:VCToolsRedistDir\vc_redist.x64.exe" .;
$VCRTVersion = (Get-Item "$Env:VCToolsRedistDir\vc_redist.x64.exe").VersionInfo;
# Build the installer
ISCC `
/DWORKING_DIR="$($pwd.Path)\" `
/DINSTALLER_BASE_NAME="$installerBaseName" `
/DSHIPPED_VCRT_MINOR="$($VCRTVersion.FileMinorPart)" `
/DSHIPPED_VCRT_VERSION="$($VCRTVersion.FileDescription)" `
$defines `
/O. `
"$PSScriptRoot\chatterino-installer.iss";
Move-Item "$installerBaseName.exe" "$installerBaseName$($Env:VARIANT_SUFFIX).exe"

View file

@ -0,0 +1,134 @@
; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#define MyAppName "Chatterino"
#define MyAppVersion "2.5.1"
#define MyAppPublisher "Chatterino Team"
#define MyAppURL "https://www.chatterino.com"
#define MyAppExeName "chatterino.exe"
; used in build-installer.ps1
; if set, must end in a backslash
#ifndef WORKING_DIR
#define WORKING_DIR ""
#endif
; Set to the build part of the VCRT version
#ifndef SHIPPED_VCRT_BUILD
#define SHIPPED_VCRT_BUILD 0
#endif
; Set to the string representation of the VCRT version
#ifndef SHIPPED_VCRT_VERSION
#define SHIPPED_VCRT_VERSION ?
#endif
[Setup]
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{F5FE6614-04D4-4D32-8600-0ABA0AC113A4}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
VersionInfoVersion={#MyAppVersion}
AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={autopf}\{#MyAppName}
DisableProgramGroupPage=yes
ArchitecturesInstallIn64BitMode=x64
;Uncomment the following line to run in non administrative install mode (install for current user only.)
;PrivilegesRequired=lowest
PrivilegesRequiredOverridesAllowed=dialog
OutputDir=out
; This is defined by the build-installer.ps1 script,
; but kept optional for regular use.
#ifdef INSTALLER_BASE_NAME
OutputBaseFilename={#INSTALLER_BASE_NAME}
#else
OutputBaseFilename=Chatterino.Installer
#endif
Compression=lzma
SolidCompression=yes
WizardStyle=modern
UsePreviousTasks=no
UninstallDisplayIcon={app}\{#MyAppExeName}
RestartIfNeededByRun=no
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
#ifdef IS_NIGHTLY
[Messages]
SetupAppTitle=Setup (Nightly)
SetupWindowTitle=Setup - %1 (Nightly)
#endif
[Tasks]
; Only show this option if the VCRT can be updated.
Name: "vcredist"; Description: "Install the required {#SHIPPED_VCRT_VERSION} ({code:VCRTDescription})"; Check: NeedsNewVCRT();
; GroupDescription: "{cm:AdditionalIcons}";
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; Flags: unchecked
Name: "freshinstall"; Description: "Fresh install (delete old settings/logs)"; Flags: unchecked
[Files]
Source: "{#WORKING_DIR}Chatterino2\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "{#WORKING_DIR}vc_redist.x64.exe"; DestDir: "{tmp}"; Tasks: vcredist;
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
[Run]
; VC++ redistributable
Filename: {tmp}\vc_redist.x64.exe; Parameters: "/install /passive /norestart"; StatusMsg: "Installing 64-bit Windows Universal Runtime..."; Flags: waituntilterminated; Tasks: vcredist
; Run chatterino
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
[InstallDelete]
; Delete cache on install
Type: filesandordirs; Name: "{userappdata}\Chatterino2\Cache"
; Delete %appdata%\Chatterino2 on freshinstall
Type: filesandordirs; Name: "{userappdata}\Chatterino2"; Tasks: freshinstall
[UninstallDelete]
; Delete cache on uninstall
Type: filesandordirs; Name: "{userappdata}\Chatterino2\Cache"
[Code]
// Get the VCRT version as a string. Null if the version could not be found.
function GetVCRT(): Variant;
var
VCRTVersion: String;
begin
Result := Null;
if RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64', 'Version', VCRTVersion) then
Result := VCRTVersion;
end;
// Gets a description about the VCRT installed vs shipped.
// This doesn't compare the versions.
function VCRTDescription(Param: String): String;
var
VCRTVersion: Variant;
begin
VCRTVersion := GetVCRT;
if VarIsNull(VCRTVersion) then
Result := 'none is installed'
else
Result := VCRTVersion + ' is installed';
end;
// Checks if a new VCRT is needed by comparing the minor version (the major one is locked at 14).
function NeedsNewVCRT(): Boolean;
var
VCRTBuild: Cardinal;
begin
Result := True;
if RegQueryDWordValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64', 'Minor', VCRTBuild) then
begin
if VCRTBuild >= {#SHIPPED_VCRT_MINOR} then
Result := False;
end;
end;

View file

@ -0,0 +1,9 @@
[Flatpak Ref]
Name=com.chatterino.chatterino
Branch=beta
Title=com.chatterino.chatterino from flathub
IsRuntime=false
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

30
.CI/deploy-crt.ps1 Normal file
View file

@ -0,0 +1,30 @@
param (
[string] $InstallDir = "Chatterino2"
)
if ($null -eq $Env:VCToolsRedistDir) {
Write-Error "VCToolsRedistDir is not set. Forgot to set Visual Studio environment variables?";
exit 1
}
# A path to the runtime libraries (e.g. "$Env:VCToolsRedistDir\onecore\x64\Microsoft.VC143.CRT")
$vclibs = (Get-ChildItem "$Env:VCToolsRedistDir\onecore\x64" -Filter '*.CRT')[0].FullName;
# All executables and libraries in the installation directory
$targets = Get-ChildItem -Recurse -Include '*.dll', '*.exe' $InstallDir;
# All dependencies of the targets (with duplicates)
$all_deps = $targets | ForEach-Object { (dumpbin /DEPENDENTS $_.FullName) -match '^(?!Dump of).+\.dll$' } | ForEach-Object { $_.Trim() };
# All dependencies without duplicates
$dependencies = $all_deps | Sort-Object -Unique;
$n_deployed = 0;
foreach ($dll in $dependencies) {
Write-Output "Checking for $dll";
if (Test-Path -PathType Leaf "$vclibs\$dll") {
Write-Output "Deploying $dll";
Copy-Item "$vclibs\$dll" "$InstallDir\$dll" -Force;
$n_deployed++;
}
}
Write-Output "Deployed $n_deployed libraries";

View file

@ -0,0 +1,61 @@
from datetime import datetime, timezone
import os
import subprocess
import re
LINE_REGEX = re.compile(
r"""(?x)
^(?P<commit>[A-Fa-f0-9]+)\s+
\(
<(?P<email>[^>]+)>\s+
(?P<date>[^\s]+\s[^\s]+\s[^\s]+)\s+
(?P<line>\d+)
\)\s
(?P<content>.*)$
"""
)
VERSION_REGEX = re.compile(r"^#+\s*v?\d")
# contains lines in the form of
# {commit-sha} (<{email}>\s+{date}\s+{line-no}) {line}
p = subprocess.run(
["git", "blame", "-e", "--date=iso", "../CHANGELOG.md"],
cwd=os.path.dirname(os.path.realpath(__file__)),
text=True,
check=True,
capture_output=True,
)
unreleased_lines: list[tuple[datetime, str]] = []
for line in p.stdout.splitlines():
if not line:
continue
m = LINE_REGEX.match(line)
assert m, f"Failed to match '{line}'"
content = m.group("content")
if not content:
continue
if content.startswith("#"):
if VERSION_REGEX.match(content):
break
continue # ignore lines with '#'
d = datetime.fromisoformat(m.group("date"))
d = d.astimezone(tz=timezone.utc)
content = content.replace("- ", f"- [{d.strftime('%Y-%m-%d')}] ", 1)
unreleased_lines.append((d, content))
unreleased_lines.sort(key=lambda it: it[0], reverse=True)
if len(unreleased_lines) == 0:
print("No changes since last release.")
for _, line in unreleased_lines[:5]:
print(line)
if len(unreleased_lines) > 5:
print("<details><summary>More Changes</summary>\n")
for _, line in unreleased_lines[5:]:
print(line)
print("</details>")

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

34
.CI/setup-clang-tidy.sh Executable file
View file

@ -0,0 +1,34 @@
#!/bin/bash
set -ev;
# aqt installs into .qtinstall/Qt/<version>/gcc_64
# This is doing the same as jurplel/install-qt-action
# See https://github.com/jurplel/install-qt-action/blob/74ca8cd6681420fc8894aed264644c7a76d7c8cb/action/src/main.ts#L52-L74
qtpath=$(echo .qtinstall/Qt/[0-9]*/*/bin/qmake | sed -e s:/bin/qmake$::)
export LD_LIBRARY_PATH="$qtpath/lib"
export QT_ROOT_DIR=$qtpath
export QT_PLUGIN_PATH="$qtpath/plugins"
export PATH="$PATH:$(realpath "$qtpath/bin")"
export Qt6_DIR="$(realpath "$qtpath")"
cmake -S. -Bbuild-clang-tidy \
-DCMAKE_BUILD_TYPE=Debug \
-DPAJLADA_SETTINGS_USE_BOOST_FILESYSTEM=On \
-DUSE_PRECOMPILED_HEADERS=OFF \
-DCMAKE_EXPORT_COMPILE_COMMANDS=On \
-DCHATTERINO_LTO=Off \
-DCHATTERINO_PLUGINS=On \
-DBUILD_WITH_QT6=On \
-DBUILD_TESTS=On \
-DBUILD_BENCHMARKS=On
# Run MOC and UIC
# This will compile the dependencies
# Get the targets using `ninja -t targets | grep autogen`
cmake --build build-clang-tidy --parallel -t \
Core_autogen \
LibCommuni_autogen \
Model_autogen \
Util_autogen \
chatterino-lib_autogen

View file

@ -1,13 +1,26 @@
freebsd_instance:
image: freebsd-12-1-release-amd64
image_family: freebsd-14-0
task:
install_script:
- pkg install -y boost-libs git qt5-buildtools qt5-concurrent qt5-core qt5-multimedia qt5-svg qtkeychain qt5-qmake cmake qt5-linguist
- pkg install -y boost-libs git qt6-base qt6-svg qt6-5compat qt6-imageformats qtkeychain-qt6 cmake
script: |
git submodule init
git submodule update
mkdir build
cd build
cmake CMAKE_C_COMPILER="cc" -DCMAKE_CXX_COMPILER="c++" -DCMAKE_C_FLAGS="-O2 -pipe -fstack-protector-strong -fno-strict-aliasing " -DCMAKE_CXX_FLAGS="-O2 -pipe -fstack-protector-strong -fno-strict-aliasing " -DLINK_OPTIONS="-fstack-protector-strong" -DCMAKE_INSTALL_PREFIX="/usr/local" -DCMAKE_BUILD_TYPE="release" ..
c++ --version
cmake \
-DCMAKE_C_COMPILER="cc" \
-DCMAKE_CXX_COMPILER="c++" \
-DCMAKE_C_FLAGS="-O2 -pipe -fstack-protector-strong -fno-strict-aliasing " \
-DCMAKE_CXX_FLAGS="-O2 -pipe -fstack-protector-strong -fno-strict-aliasing " \
-DLINK_OPTIONS="-fstack-protector-strong" \
-DCMAKE_INSTALL_PREFIX="/usr/local" \
-DUSE_SYSTEM_QTKEYCHAIN="ON" \
-DCMAKE_BUILD_TYPE="release" \
-DCMAKE_EXPORT_COMPILE_COMMANDS="ON" \
-DBUILD_WITH_QT6="ON" \
..
cat compile_commands.json
make -j $(getconf _NPROCESSORS_ONLN)

View file

@ -9,14 +9,13 @@ AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: false
AlwaysBreakBeforeMultilineStrings: false
BasedOnStyle: Google
BraceWrapping: {
AfterClass: 'true'
AfterControlStatement: 'true'
AfterFunction: 'true'
AfterNamespace: 'false'
BeforeCatch: 'true'
BeforeElse: 'true'
}
BraceWrapping:
AfterClass: "true"
AfterControlStatement: "true"
AfterFunction: "true"
AfterNamespace: "false"
BeforeCatch: "true"
BeforeElse: "true"
BreakBeforeBraces: Custom
BreakConstructorInitializersBeforeComma: true
ColumnLimit: 80
@ -27,9 +26,28 @@ IndentCaseLabels: true
IndentWidth: 4
IndentWrappedFunctionNames: true
IndentPPDirectives: AfterHash
IncludeBlocks: Preserve
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
InsertNewlineAtEOF: true

View file

@ -1,47 +1,78 @@
Checks: '-*,
clang-diagnostic-*,
llvm-*,
misc-*,
-misc-unused-parameters,
readability-identifier-naming,
-llvm-header-guard,
modernize-*,
readability-*,
performance-*,
misc-*,
bugprone-*,
cert-*,
cppcoreguidelines-*,
-cppcoreguidelines-pro-type-cstyle-cast,
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-cppcoreguidelines-pro-type-member-init,
-cppcoreguidelines-owning-memory,
-cppcoreguidelines-avoid-magic-numbers,
-readability-magic-numbers,
-performance-noexcept-move-constructor,
-misc-non-private-member-variables-in-classes,
-cppcoreguidelines-non-private-member-variables-in-classes,
-modernize-use-nodiscard,
-modernize-use-trailing-return-type,
-readability-identifier-length,
-readability-function-cognitive-complexity,
-bugprone-easily-swappable-parameters,
'
Checks: "-*,
clang-diagnostic-*,
llvm-*,
misc-*,
-misc-unused-parameters,
readability-identifier-naming,
-llvm-header-guard,
-llvm-include-order,
modernize-*,
readability-*,
performance-*,
misc-*,
bugprone-*,
cert-*,
cppcoreguidelines-*,
-cppcoreguidelines-pro-type-cstyle-cast,
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-cppcoreguidelines-owning-memory,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-avoid-const-or-ref-data-members,
-readability-magic-numbers,
-performance-noexcept-move-constructor,
-misc-non-private-member-variables-in-classes,
-misc-no-recursion,
-cppcoreguidelines-non-private-member-variables-in-classes,
-modernize-use-nodiscard,
-modernize-use-trailing-return-type,
-readability-identifier-length,
-readability-function-cognitive-complexity,
-bugprone-easily-swappable-parameters,
-cert-err58-cpp,
-modernize-avoid-c-arrays,
-misc-include-cleaner
"
CheckOptions:
- key: readability-identifier-naming.ClassCase
value: CamelCase
- key: readability-identifier-naming.EnumCase
value: CamelCase
- key: readability-identifier-naming.FunctionCase
value: camelBack
- key: readability-identifier-naming.MemberCase
value: camelBack
- key: readability-identifier-naming.PrivateMemberSuffix
value: _
- key: readability-identifier-naming.UnionCase
value: CamelCase
- key: readability-identifier-naming.GlobalVariableCase
value: UPPER_CASE
- key: readability-identifier-naming.VariableCase
value: camelBack
- key: readability-identifier-naming.ClassCase
value: CamelCase
- key: readability-identifier-naming.EnumCase
value: CamelCase
- key: readability-identifier-naming.FunctionCase
value: camelBack
- key: readability-identifier-naming.FunctionIgnoredRegexp
value: ^TEST$
- key: readability-identifier-naming.MemberCase
value: camelBack
- key: readability-identifier-naming.PrivateMemberIgnoredRegexp
value: ^.*_$
- key: readability-identifier-naming.ProtectedMemberIgnoredRegexp
value: ^.*_$
- key: readability-identifier-naming.UnionCase
value: CamelCase
- key: readability-identifier-naming.GlobalConstantCase
value: UPPER_CASE
- key: readability-identifier-naming.GlobalVariableCase
value: UPPER_CASE
- key: readability-identifier-naming.VariableCase
value: camelBack
- key: readability-implicit-bool-conversion.AllowPointerConditions
value: true
# Lua state
- 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
- key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
value: true

7
.codecov.yml Normal file
View file

@ -0,0 +1,7 @@
comment: false
ignore:
- "/usr/local*/**/*"
- "/usr/include/**/*"
- "lib/"
- "**/ui_*.h"
- "**/moc_*.cpp"

View file

@ -0,0 +1,38 @@
FROM ubuntu:20.04
ENV TZ=UTC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN apt-get update && apt-get -y install --no-install-recommends \
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
RUN apt-get -y install \
git \
lsb-release \
python3-pip && \
apt-get clean all
# Install Qt as we do in CI
RUN pip3 install -U pip && \
pip3 install aqtinstall && \
aqt install-qt linux desktop 5.12.12 && \
mkdir -p /opt/qt512 && \
mv /5.12.12/gcc_64/* /opt/qt512

View file

@ -0,0 +1,17 @@
FROM chatterino-ubuntu-20.04-base
ADD . /src
RUN mkdir /src/build
# cmake
RUN cd /src/build && \
CXXFLAGS=-fno-sized-deallocation cmake \
-DCMAKE_INSTALL_PREFIX=appdir/usr/ \
-DCMAKE_PREFIX_PATH=/opt/qt512/lib/cmake \
-DBUILD_WITH_QTKEYCHAIN=OFF \
..
# build
RUN cd /src/build && \
make -j8

View file

@ -0,0 +1,21 @@
FROM chatterino-ubuntu-20.04-build
# In CI, this is set from the aqtinstall action
ENV Qt5_DIR=/opt/qt512
WORKDIR /src/build
ADD .CI /src/.CI
# Install dependencies necessary for AppImage packaging
RUN apt-get update && apt-get -y install --no-install-recommends \
curl \
libfontconfig \
libxrender1 \
file
# package deb
RUN ./../.CI/CreateUbuntuDeb.sh
# package appimage
RUN ./../.CI/CreateAppImage.sh

View file

@ -0,0 +1,44 @@
FROM ubuntu:22.04
ENV TZ=UTC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN apt-get update && apt-get -y install --no-install-recommends \
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 \
libfontconfig
RUN apt-get -y install \
git \
lsb-release \
python3-pip && \
apt-get clean all
# Install Qt as we do in CI
RUN pip3 install -U pip && \
pip3 install aqtinstall && \
aqt install-qt linux desktop 5.15.2 && \
mkdir -p /opt/qt515 && \
mv /5.15.2/gcc_64/* /opt/qt515
ADD ./.patches /tmp/.patches
# Apply Qt patches
RUN patch "/opt/qt515/include/QtConcurrent/qtconcurrentthreadengine.h" /tmp/.patches/qt5-on-newer-gcc.patch

View file

@ -0,0 +1,17 @@
FROM chatterino-ubuntu-22.04-base
ADD . /src
RUN mkdir /src/build
# cmake
RUN cd /src/build && \
CXXFLAGS=-fno-sized-deallocation cmake \
-DCMAKE_INSTALL_PREFIX=appdir/usr/ \
-DCMAKE_PREFIX_PATH=/opt/qt515/lib/cmake \
-DBUILD_WITH_QTKEYCHAIN=OFF \
..
# build
RUN cd /src/build && \
make -j8

View file

@ -0,0 +1,21 @@
FROM chatterino-ubuntu-22.04-build
# In CI, this is set from the aqtinstall action
ENV Qt5_DIR=/opt/qt515
WORKDIR /src/build
ADD .CI /src/.CI
# Install dependencies necessary for AppImage packaging
RUN apt-get update && apt-get -y install --no-install-recommends \
curl \
libxcb-shape0 \
libfontconfig1 \
file
# package deb
RUN ./../.CI/CreateUbuntuDeb.sh
# package appimage
RUN ./../.CI/CreateAppImage.sh

View file

@ -0,0 +1,59 @@
ARG UBUNTU_VERSION=22.04
FROM ubuntu:$UBUNTU_VERSION
ARG QT_VERSION=6.2.4
ARG BUILD_WITH_QT6=ON
ENV TZ=UTC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN apt-get update && apt-get -y install --no-install-recommends \
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 \
libfontconfig1-dev
RUN apt-get -y install \
git \
lsb-release \
python3-pip && \
apt-get clean all
# Install Qt as we do in CI
RUN pip3 install -U pip && \
pip3 install aqtinstall && \
aqt install-qt linux desktop $QT_VERSION -O /opt/qt --modules qt5compat
ADD . /src
RUN mkdir /src/build
# cmake
RUN cd /src/build && \
CXXFLAGS=-fno-sized-deallocation cmake \
-DBUILD_WITH_QT6=$BUILD_WITH_QT6 \
-DCMAKE_INSTALL_PREFIX=appdir/usr/ \
-DCMAKE_PREFIX_PATH=/opt/qt/$QT_VERSION/gcc_64/lib/cmake \
-DBUILD_WITH_QTKEYCHAIN=OFF \
..
# build
RUN cd /src/build && \
make -j8

View file

@ -0,0 +1,23 @@
ARG UBUNTU_VERSION=22.04
FROM chatterino-ubuntu-$UBUNTU_VERSION-qt6-build
# In CI, this is set from the aqtinstall action
ENV Qt6_DIR=/opt/qt/6.2.4/gcc_64
WORKDIR /src/build
ADD .CI /src/.CI
# Install dependencies necessary for AppImage packaging
RUN apt-get update && apt-get -y install --no-install-recommends \
curl \
libxcb-shape0 \
libfontconfig1 \
file
# package deb
RUN ./../.CI/CreateUbuntuDeb.sh
# package appimage
RUN ./../.CI/CreateAppImage.sh

View file

@ -0,0 +1,24 @@
FROM chatterino-ubuntu-22.04-base
ADD . /src
RUN mkdir /src/build
# cmake
RUN cd /src/build && \
CXXFLAGS=-fno-sized-deallocation cmake \
-DCMAKE_PREFIX_PATH=/opt/qt515/lib/cmake \
-DUSE_PRECOMPILED_HEADERS=OFF \
-DBUILD_WITH_QTKEYCHAIN=OFF \
-DBUILD_TESTS=ON \
..
# build
RUN cd /src/build && \
make -j8
ENV QT_QPA_PLATFORM=minimal
ENV QT_PLUGIN_PATH=/opt/qt515/plugins
# test
CMD /src/build/bin/chatterino-test

42
.docker/README.md Normal file
View file

@ -0,0 +1,42 @@
## Groups
### Ubuntu 20.04 package
`Dockerfile-ubuntu-20.04-package` relies on `Dockerfile-ubuntu-20.04-build`
To build, from the repo root
1. Build a docker image that contains all the dependencies necessary to build Chatterino on Ubuntu 20.04
`docker buildx build -t chatterino-ubuntu-20.04-base -f .docker/Dockerfile-ubuntu-20.04-base .`
1. Build a docker image that contains all the build artifacts and source from building Chatterino on Ubuntu 20.04
`docker buildx build -t chatterino-ubuntu-20.04-build -f .docker/Dockerfile-ubuntu-20.04-build .`
1. Build a docker image that uses the above-built image & packages it into a .deb file
`docker buildx build -t chatterino-ubuntu-20.04-package -f .docker/Dockerfile-ubuntu-20.04-package .`
To extract the final package, you can run the following command:
`docker run -v $PWD:/opt/mount --rm -it chatterino-ubuntu-20.04-package bash -c "cp /src/build/Chatterino-x86_64.deb /opt/mount/"`
### Ubuntu 22.04 package
`Dockerfile-ubuntu-22.04-package` relies on `Dockerfile-ubuntu-22.04-build`
To build, from the repo root
1. Build a docker image that contains all the dependencies necessary to build Chatterino on Ubuntu 22.04
`docker buildx build -t chatterino-ubuntu-22.04-base -f .docker/Dockerfile-ubuntu-22.04-base .`
1. Build a docker image that contains all the build artifacts and source from building Chatterino on Ubuntu 22.04
`docker buildx build -t chatterino-ubuntu-22.04-build -f .docker/Dockerfile-ubuntu-22.04-build .`
1. Build a docker image that uses the above-built image & packages it into a .deb file
`docker buildx build -t chatterino-ubuntu-22.04-package -f .docker/Dockerfile-ubuntu-22.04-package .`
To extract the final package, you can run the following command:
`docker run -v $PWD:/opt/mount --rm -it chatterino-ubuntu-22.04-package bash -c "cp /src/build/Chatterino-x86_64.deb /opt/mount/"`
NOTE: The AppImage from Ubuntu 22.04 is broken. Approach with caution
#### Testing
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`

4
.dockerignore Normal file
View file

@ -0,0 +1,4 @@
build*
.mypy_cache
.cache
.docker

14
.git-blame-ignore-revs Normal file
View file

@ -0,0 +1,14 @@
# If a commit modifies a ton of files and doesn't really contribute to the
# output of git-blame, please add it here
#
# Don't add commits from the same PR you are creating. We squash PRs into a
# single commit, so references to those commits will be lost
#
# 2018 - changed to 80 max column
f71ff08e686ae76c3dd4084d0f05f27ba9b3fdcb
#
# 2018 - added brace wrapping after if and for
e259b9e39f46f3cb0e4838c988d4f320a03dfaa4
#
# 2019 - Normalize line endings in already existing files
b06eb9df835c25154899fbcf43e9b37addcea1b1

2
.github/FUNDING.yml vendored
View file

@ -1 +1 @@
custom: "https://streamelements.com/fourtf/tip"
custom: "https://streamelements.com/fourtf/tip"

View file

@ -6,48 +6,47 @@ body:
- type: checkboxes
id: acknowledgments
attributes:
label: Checklist
description:
options:
- label: I'm reporting a problem with Chatterino
required: true
- label: I've verified that I'm running **the most** recent nightly build or stable release
required: true
- label: I've looked for my problem on the [wiki](https://wiki.chatterino.com/Help/)
required: true
- label: I've searched the [issues and pull requests](https://github.com/Chatterino/chatterino2/issues?q=) for similar looking reports
required: true
label: Checklist
description:
options:
- label: I'm reporting a problem with Chatterino
required: true
- label: I've verified that I'm running **the most** recent nightly build or stable release
required: true
- label: I've looked for my problem on the [wiki](https://wiki.chatterino.com/Help/)
required: true
- label: I've searched the [issues and pull requests](https://github.com/Chatterino/chatterino2/issues?q=) for similar looking reports
required: true
- type: textarea
id: description
validations:
required: true
required: true
attributes:
label: Describe your issue
description: |
Write a brief description of your issue.
Important:
Focus on the problem instead of a concrete solution. This ensures that the focus of the thread is to resolve your issue.
If you want to voice a concrete idea you can add a comment below after posting the issue.
placeholder: |
Examples:
- I cannot do X.
- I have trouble doing X.
- Feature X has stopped working for me.
label: Describe your issue
description: |
Write a brief description of your issue.
Important:
Focus on the problem instead of a concrete solution. This ensures that the focus of the thread is to resolve your issue.
If you want to voice a concrete idea you can add a comment below after posting the issue.
placeholder: |
Examples:
- I cannot do X.
- I have trouble doing X.
- Feature X has stopped working for me.
- type: textarea
id: screenshots
attributes:
label: Screenshots
description: While optional, it's highly encouraged to include screenshots or videos to illustrate what you mean.
placeholder: You can upload them using the text editor's dedicated button.
label: Screenshots
description: While optional, it's highly encouraged to include screenshots or videos to illustrate what you mean.
placeholder: You can upload them using the text editor's dedicated button.
- type: input
id: versions
validations:
required: true
required: true
attributes:
label: OS and Chatterino Version
description: The name of your Operating System and the version shown in Chatterino's about settings page (⚙ -> about tab).
placeholder: Chatterino 2.3.5 (commit 81a62764, 2022-04-05) on Windows 10 Version 2009, kernel 10.0.19043
label: OS and Chatterino Version
description: The name of your Operating System and the version shown in Chatterino's about settings page (⚙ -> about tab).
placeholder: Chatterino 2.3.5 (commit 81a62764, 2022-04-05) on Windows 10 Version 2009, kernel 10.0.19043

View file

@ -1,11 +1,11 @@
blank_issues_enabled: false
contact_links:
- name: Issue about the Chatterino Browser Extension
url: https://github.com/Chatterino/chatterino-browser-ext/issues
about: Make a suggestion or report a bug about the Chatterino browser extension.
- name: Suggestions or feature request
url: https://github.com/chatterino/chatterino2/discussions/categories/ideas
about: Got something you think should change or be added? Search for or start a new discussion!
- name: Help
url: https://github.com/chatterino/chatterino2/discussions/categories/q-a
about: Chatterino2 not working as you'd expect? Not sure it's a bug? Check the Q&A section!
- name: Issue about the Chatterino Browser Extension
url: https://github.com/Chatterino/chatterino-browser-ext/issues
about: Make a suggestion or report a bug about the Chatterino browser extension.
- name: Suggestions or feature request
url: https://github.com/chatterino/chatterino2/discussions/categories/ideas
about: Got something you think should change or be added? Search for or start a new discussion!
- name: Help
url: https://github.com/chatterino/chatterino2/discussions/categories/q-a
about: Chatterino2 not working as you'd expect? Not sure it's a bug? Check the Q&A section!

View file

@ -1,7 +1,5 @@
Pull request checklist:
- [ ] `CHANGELOG.md` was updated, if applicable
# 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

@ -5,305 +5,435 @@ on:
push:
branches:
- master
- "bugfix-release/*"
- "release/*"
pull_request:
workflow_dispatch:
merge_group:
concurrency:
concurrency:
group: build-${{ github.ref }}
cancel-in-progress: true
env:
C2_ENABLE_LTO: ${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/bugfix-release/') || startsWith(github.ref, 'refs/heads/release/') }}
CHATTERINO_REQUIRE_CLEAN_GIT: On
C2_BUILD_WITH_QT6: Off
# Last known good conan version
# 2.0.3 has a bug on Windows (conan-io/conan#13606)
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.7.2
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.7.2
force-lto: false
plugins: true
skip-artifact: false
skip-crashpad: false
build-appimage: true
build-deb: true
- os: ubuntu-24.04
container: ghcr.io/chatterino/chatterino2-build-ubuntu-24.04:latest
qt-version: 6.7.2
force-lto: false
plugins: true
skip-artifact: false
skip-crashpad: false
build-appimage: false
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: Fix git permission error
run: |
git config --global --add safe.directory '*'
- 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:
os: [windows-latest, ubuntu-latest, macos-latest]
qt-version: [5.15.2, 5.12.12]
pch: [true]
include:
- os: ubuntu-latest
# macOS
- os: macos-13
qt-version: 5.15.2
pch: false
force-lto: false
plugins: true
skip-artifact: false
skip-crashpad: false
# Windows
- os: windows-latest
qt-version: 6.7.1
force-lto: false
plugins: true
skip-artifact: false
skip-crashpad: false
# Windows 7/8
- os: windows-latest
qt-version: 5.15.2
force-lto: false
plugins: true
skip-artifact: false
skip-crashpad: true
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.') }}
C2_USE_OPENSSL3: ${{ startsWith(matrix.qt-version, '6.') && 'True' || 'False' }}
C2_CONAN_CACHE_SUFFIX: ${{ startsWith(matrix.qt-version, '6.') && '-QT6' || '' }}
steps:
- name: Set environment variables for windows-latest
if: matrix.os == 'windows-latest'
run: |
echo "vs_version=2022" >> $GITHUB_ENV
shell: bash
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: true
fetch-depth: 0 # allows for tags access
submodules: recursive
fetch-depth: 0 # allows for tags access
- name: Cache Qt
id: cache-qt
uses: actions/cache@v3
- name: Install Qt5
if: startsWith(matrix.qt-version, '5.')
uses: jurplel/install-qt-action@v4.0.0
with:
path: "${{ github.workspace }}/qt/"
key: ${{ runner.os }}-QtCache-${{ matrix.qt-version }}
# LINUX
- name: Install Qt
uses: jurplel/install-qt-action@v2
with:
cached: ${{ steps.cache-qt.outputs.cache-hit }}
cache: true
cache-key-prefix: ${{ runner.os }}-QtCache-${{ matrix.qt-version }}-v2
version: ${{ matrix.qt-version }}
- name: Install Qt6
if: startsWith(matrix.qt-version, '6.')
uses: jurplel/install-qt-action@v4.0.0
with:
cache: true
cache-key-prefix: ${{ runner.os }}-QtCache-${{ matrix.qt-version }}-v2
modules: qt5compat qtimageformats
version: ${{ matrix.qt-version }}
dir: "${{ github.workspace }}/qt/"
# WINDOWS
- name: Cache conan packages part 1
- name: Enable Developer Command Prompt (Windows)
if: startsWith(matrix.os, 'windows')
uses: actions/cache@v3
with:
key: ${{ runner.os }}-conan-user-${{ hashFiles('**/conanfile.txt') }}
path: ~/.conan/
uses: ilammy/msvc-dev-cmd@v1.13.0
- name: Cache conan packages part 2
- name: Setup sccache (Windows)
# sccache v0.7.4
uses: hendrikmuhs/ccache-action@v1.2.14
if: startsWith(matrix.os, 'windows')
uses: actions/cache@v3
with:
key: ${{ runner.os }}-conan-root-${{ hashFiles('**/conanfile.txt') }}
path: C:/.conan/
variant: sccache
# only save on on the default (master) branch
save: ${{ github.event_name == 'push' }}
key: sccache-build-${{ matrix.os }}-${{ matrix.qt-version }}-${{ matrix.skip-crashpad }}
restore-keys: |
sccache-build-${{ matrix.os }}-${{ matrix.qt-version }}
- name: Add Conan to path
- name: Cache conan packages (Windows)
if: startsWith(matrix.os, 'windows')
run: echo "C:\Program Files\Conan\conan\" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
uses: actions/cache@v4
with:
key: ${{ runner.os }}-conan-user-${{ hashFiles('**/conanfile.py') }}${{ env.C2_CONAN_CACHE_SUFFIX }}
path: ~/.conan2/
- name: Install Conan (Windows)
if: startsWith(matrix.os, 'windows')
run: |
python3 -c "import site; import sys; print(f'{site.USER_BASE}\\Python{sys.version_info.major}{sys.version_info.minor}\\Scripts')" >> "$GITHUB_PATH"
pip3 install --user "conan==${{ env.CONAN_VERSION }}"
shell: powershell
- name: Setup Conan (Windows)
if: startsWith(matrix.os, 'windows')
run: |
conan --version
conan profile detect -f
shell: powershell
- name: Install dependencies (Windows)
if: startsWith(matrix.os, 'windows')
run: |
choco install conan -y
- name: Enable Developer Command Prompt
if: startsWith(matrix.os, 'windows')
uses: ilammy/msvc-dev-cmd@v1.10.0
mkdir build
cd build
conan install .. `
-s build_type=RelWithDebInfo `
-c tools.cmake.cmaketoolchain:generator="NMake Makefiles" `
-b missing `
--output-folder=. `
-o with_openssl3="$Env:C2_USE_OPENSSL3"
shell: powershell
- name: Build (Windows)
if: startsWith(matrix.os, 'windows')
shell: pwsh
run: |
mkdir build
cd build
conan install .. -b missing
cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DUSE_CONAN=ON ..
set cl=/MP
nmake /S /NOLOGO
windeployqt bin/chatterino.exe --release --no-compiler-runtime --no-translations --no-opengl-sw --dir Chatterino2/
cp bin/chatterino.exe Chatterino2/
echo nightly > Chatterino2/modes
7z a chatterino-windows-x86-64.zip Chatterino2/
- name: Upload artifact (Windows)
if: startsWith(matrix.os, 'windows')
uses: actions/upload-artifact@v3
with:
name: chatterino-windows-x86-64-${{ matrix.qt-version }}.zip
path: build/chatterino-windows-x86-64.zip
- name: Clean Conan pkgs
if: startsWith(matrix.os, 'windows')
run: conan remove "*" -fsb
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 \
libssl-dev \
libboost-dev \
libxcb-randr0-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: Build (Ubuntu)
if: startsWith(matrix.os, 'ubuntu')
run: |
mkdir build
cd build
cmake \
-DCMAKE_INSTALL_PREFIX=appdir/usr/ \
-DCMAKE_BUILD_TYPE=Release \
-DPAJLADA_SETTINGS_USE_BOOST_FILESYSTEM=On \
-DUSE_PRECOMPILED_HEADERS=${{ matrix.pch }} \
-DCMAKE_EXPORT_COMPILE_COMMANDS=On \
cd build
cmake `
-G"NMake Makefiles" `
-DCMAKE_BUILD_TYPE=RelWithDebInfo `
-DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" `
-DUSE_PRECOMPILED_HEADERS=ON `
-DBUILD_WITH_CRASHPAD="$Env:C2_ENABLE_CRASHPAD" `
-DCHATTERINO_LTO="$Env:C2_ENABLE_LTO" `
-DCHATTERINO_PLUGINS="$Env:C2_PLUGINS" `
-DBUILD_WITH_QT6="$Env:C2_BUILD_WITH_QT6" `
..
make -j$(nproc)
shell: bash
set cl=/MP
nmake /S /NOLOGO
- name: clang-tidy review
if: (startsWith(matrix.os, 'ubuntu') && matrix.pch == false && matrix.qt-version == '5.15.2' && github.event_name == 'pull_request')
uses: ZedThree/clang-tidy-review@v0.9.0
id: review
with:
build_dir: build
config_file: '.clang-tidy'
- name: Package - AppImage (Ubuntu)
if: startsWith(matrix.os, 'ubuntu')
- name: Build crashpad (Windows)
if: startsWith(matrix.os, 'windows') && !matrix.skip-crashpad
shell: pwsh
run: |
cd build
sh ./../.CI/CreateAppImage.sh
shell: bash
cd build
set cl=/MP
nmake /S /NOLOGO chatterino-crash-handler
mkdir Chatterino2/crashpad
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: Package - .deb (Ubuntu)
if: startsWith(matrix.os, 'ubuntu')
- name: Prepare build dir (windows)
if: startsWith(matrix.os, 'windows')
run: |
cd build
sh ./../.CI/CreateUbuntuDeb.sh
cd build
windeployqt bin/chatterino.exe --release --no-compiler-runtime --no-translations --no-opengl-sw --dir Chatterino2/
cp bin/chatterino.exe Chatterino2/
..\.CI\deploy-crt.ps1 Chatterino2
echo nightly > Chatterino2/modes
- name: Package (windows)
if: startsWith(matrix.os, 'windows')
working-directory: build
run: |
7z a chatterino-windows-x86-64-Qt-${{ matrix.qt-version }}.zip Chatterino2/
- name: Upload artifact (Windows - binary)
if: startsWith(matrix.os, 'windows') && !matrix.skip-artifact
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@v4
with:
name: chatterino-windows-x86-64-Qt-${{ matrix.qt-version }}-symbols.pdb.7z
path: build/bin/chatterino-Qt-${{ matrix.qt-version }}.pdb.7z
- name: Clean Conan cache
if: startsWith(matrix.os, 'windows')
run: conan cache clean --source --build --download "*"
shell: bash
- name: Upload artifact - AppImage (Ubuntu)
if: startsWith(matrix.os, 'ubuntu')
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')
uses: actions/upload-artifact@v3
with:
name: Chatterino-${{ matrix.qt-version }}.deb
path: build/Chatterino.deb
# MACOS
- name: Install dependencies (MacOS)
if: startsWith(matrix.os, 'macos')
run: |
brew install boost openssl rapidjson p7zip create-dmg cmake tree
brew install boost openssl rapidjson p7zip create-dmg cmake tree
shell: bash
- name: Build (MacOS)
if: startsWith(matrix.os, 'macos')
run: |
mkdir build
cd build
cmake \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 \
-DOPENSSL_ROOT_DIR=/usr/local/opt/openssl \
-DUSE_PRECOMPILED_HEADERS=${{ matrix.pch }} \
..
make -j$(sysctl -n hw.logicalcpu)
mkdir build
cd build
cmake \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 \
-DOPENSSL_ROOT_DIR=/usr/local/opt/openssl \
-DUSE_PRECOMPILED_HEADERS=OFF \
-DCHATTERINO_LTO="$C2_ENABLE_LTO" \
-DCHATTERINO_PLUGINS="$C2_PLUGINS" \
-DBUILD_WITH_QT6="$C2_BUILD_WITH_QT6" \
..
make -j"$(sysctl -n hw.logicalcpu)"
shell: bash
- name: Package (MacOS)
if: startsWith(matrix.os, 'macos')
env:
OUTPUT_DMG_PATH: chatterino-macos-Qt-${{ matrix.qt-version}}.dmg
run: |
ls -la
pwd
ls -la build || true
cd build
sh ./../.CI/CreateDMG.sh
ls -la
pwd
ls -la build || true
cd build
./../.CI/MacDeploy.sh
./../.CI/CreateDMG.sh
shell: bash
- name: Upload artifact (MacOS)
if: startsWith(matrix.os, 'macos')
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: chatterino-osx-${{ matrix.qt-version }}.dmg
path: build/chatterino-osx.dmg
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')
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # allows for tags access
# Windows
- uses: actions/download-artifact@v4
name: Windows Qt6.7.1
with:
name: chatterino-windows-x86-64-Qt-6.7.1.zip
path: release-artifacts/
- uses: actions/download-artifact@v4
name: Windows Qt6.7.1 symbols
with:
name: chatterino-windows-x86-64-Qt-6.7.1-symbols.pdb.7z
path: release-artifacts/
- uses: actions/download-artifact@v4
name: Windows Qt5.15.2
with:
name: chatterino-windows-x86-64-Qt-5.15.2.zip
path: release-artifacts/
# Linux
- uses: actions/download-artifact@v4
name: Linux AppImage
with:
name: Chatterino-x86_64-Qt-6.7.2.AppImage
path: release-artifacts/
- uses: actions/download-artifact@v4
name: Ubuntu 20.04 deb
with:
name: Chatterino-ubuntu-20.04-Qt-6.7.2.deb
path: release-artifacts/
- uses: actions/download-artifact@v4
name: Ubuntu 22.04 deb
with:
name: Chatterino-ubuntu-22.04-Qt-6.7.2.deb
path: release-artifacts/
- uses: actions/download-artifact@v4
name: Ubuntu 24.04 deb
with:
name: Chatterino-ubuntu-24.04-Qt-6.7.2.deb
path: release-artifacts/
- name: Copy flatpakref
run: |
cp .CI/chatterino-nightly.flatpakref release-artifacts/
shell: bash
- name: Rename artifacts
run: |
ls -l
# 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: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Format changes
id: format-changes
run: |
delimiter=$(openssl rand -hex 32)
{
echo "changelog<<$delimiter"
python3 ./.CI/format-recent-changes.py
echo $delimiter
} >> "$GITHUB_OUTPUT"
shell: bash
- name: Create release
id: create_release
uses: pajlada/create-release@v2.0.3
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: ncipollo/release-action@v1.14.0
with:
tag_name: nightly-build
backup_tag_name: backup-nightly-build
release_name: Nightly Release
body: |
Nightly Build
replacesArtifacts: true
allowUpdates: true
artifactErrorsFailBuild: true
artifacts: "release-artifacts/*"
body: ${{ steps.format-changes.outputs.changelog }}
prerelease: true
name: Nightly Release
tag: nightly-build
- uses: actions/download-artifact@v3
with:
name: chatterino-windows-x86-64-5.15.2.zip
path: windows/
- uses: actions/download-artifact@v3
with:
name: Chatterino-x86_64-5.15.2.AppImage
path: linux/
- uses: actions/download-artifact@v3
with:
name: Chatterino-5.15.2.deb
path: ubuntu/
- uses: actions/download-artifact@v3
with:
name: chatterino-osx-5.15.2.dmg
path: macos/
# TODO: Extract dmg and appimage
# - name: Read upload URL into output
# id: upload_url
# run: |
# echo "::set-output name=upload_url::$(cat release-upload-url.txt/release-upload-url.txt)"
- name: Upload release asset (Windows)
uses: actions/upload-release-asset@v1.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./windows/chatterino-windows-x86-64.zip
asset_name: chatterino-windows-x86-64.zip
asset_content_type: application/zip
- name: Upload release asset (Ubuntu)
uses: actions/upload-release-asset@v1.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./linux/Chatterino-x86_64.AppImage
asset_name: Chatterino-x86_64.AppImage
asset_content_type: application/x-executable
- name: Upload release asset (Ubuntu .deb)
uses: actions/upload-release-asset@v1.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./ubuntu/Chatterino.deb
asset_name: Chatterino-x86_64.deb
asset_content_type: application/vnd.debian.binary-package
- name: Upload release asset (MacOS)
uses: actions/upload-release-asset@v1.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./macos/chatterino-osx.dmg
asset_name: chatterino-osx.dmg
asset_content_type: application/x-bzip2
- name: Update nightly-build tag
run: |
git tag -f nightly-build
git push -f origin nightly-build
shell: bash

View file

@ -0,0 +1,33 @@
---
name: Changelog Category Check
on:
pull_request:
types:
- labeled
- unlabeled
- opened
- synchronize
- reopened
jobs:
changelog-category-check:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v7
id: label-checker
with:
result-encoding: "string"
script: |
const response = await github.rest.issues.listLabelsOnIssue({
issue_number: context.payload.pull_request.number,
owner: context.repo.owner,
repo: context.repo.repo
});
if (new Set(response.data.map(label => label.name)).has("skip-changelog-checker")) {
return "skip";
}
return "";
- uses: pajlads/changelog-checker@v1.0.1
if: steps.label-checker.outputs.result != 'skip'

View file

@ -2,8 +2,8 @@ name: Changelog Check
on:
pull_request:
branches: [ master ]
types: [ opened, synchronize, reopened, ready_for_review, labeled, unlabeled ]
branches: [master]
types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled]
jobs:
check-changelog:
@ -13,5 +13,5 @@ jobs:
- name: Changelog check
uses: dangoslen/changelog-enforcer@v3
with:
changeLogPath: 'CHANGELOG.md'
skipLabels: 'no changelog entry needed, ci, submodules'
changeLogPath: "CHANGELOG.md"
skipLabels: "no changelog entry needed, ci, submodules"

View file

@ -6,26 +6,31 @@ on:
branches:
- master
pull_request:
merge_group:
concurrency:
concurrency:
group: check-formatting-${{ github.ref }}
cancel-in-progress: true
jobs:
check:
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: apt-get update
run: sudo apt-get update
- name: Install clang-format
run: sudo apt-get -y install clang-format dos2unix
run: sudo apt-get -y install dos2unix
- name: Check formatting
run: ./tools/check-format.sh
uses: DoozyX/clang-format-lint-action@v0.18
with:
source: "./src ./tests/src ./benchmarks/src ./mocks/include"
extensions: "hpp,cpp"
clangFormatVersion: 16
- name: Check line-endings
run: ./tools/check-line-endings.sh
run: ./scripts/check-line-endings.sh

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

@ -0,0 +1,65 @@
---
name: clang-tidy
on:
pull_request:
concurrency:
group: clang-tidy-${{ github.ref }}
cancel-in-progress: true
jobs:
review:
name: "clang-tidy ${{ matrix.os }}, Qt ${{ matrix.qt-version }})"
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
# Ubuntu 22.04, Qt 6.6
- os: ubuntu-22.04
qt-version: 6.6.2
fail-fast: false
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0 # allows for tags access
- name: Install Qt6
if: startsWith(matrix.qt-version, '6.')
uses: jurplel/install-qt-action@v4.0.0
with:
cache: true
cache-key-prefix: ${{ runner.os }}-QtCache-${{ matrix.qt-version }}-v2
modules: qt5compat qtimageformats
version: ${{ matrix.qt-version }}
dir: ${{ github.workspace }}/.qtinstall
set-env: false
- name: clang-tidy review
timeout-minutes: 20
uses: ZedThree/clang-tidy-review@v0.19.0
with:
build_dir: build-clang-tidy
config_file: ".clang-tidy"
split_workflow: true
exclude: "lib/*,tools/crash-handler/*"
cmake_command: >-
./.CI/setup-clang-tidy.sh
apt_packages: >-
libsecret-1-dev,
libboost-dev, libboost-system-dev, libboost-filesystem-dev,
libssl-dev,
rapidjson-dev,
libbenchmark-dev,
build-essential,
libgl1-mesa-dev, libgstreamer-gl1.0-0, libpulse-dev,
libxcb-glx0, libxcb-icccm4, libxcb-image0, libxcb-keysyms1, libxcb-randr0,
libxcb-render-util0, libxcb-render0, libxcb-shape0, libxcb-shm0, libxcb-sync1,
libxcb-util1, libxcb-xfixes0, libxcb-xinerama0, libxcb1, libxkbcommon-dev,
libxkbcommon-x11-0, libxcb-xkb-dev, libxcb-cursor0
- name: clang-tidy-review upload
uses: ZedThree/clang-tidy-review/upload@v0.19.0

58
.github/workflows/create-installer.yml vendored Normal file
View file

@ -0,0 +1,58 @@
name: Create installer
on:
workflow_run:
workflows: ["Build"]
types: [completed]
# make sure this only runs on the default branch
branches:
- master
- "bugfix-release/*"
- "release/*"
workflow_dispatch:
jobs:
create-installer:
runs-on: windows-latest
# Only run manually or when a build succeeds
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
strategy:
matrix:
qt-version: ["6.7.1"]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # allows for tags access
- name: Download artifact
uses: dawidd6/action-download-artifact@v6
with:
workflow: build.yml
name: chatterino-windows-x86-64-Qt-${{ matrix.qt-version }}.zip
commit: ${{ github.sha }}
path: build/
- name: Unzip
run: 7z e -spf chatterino-windows-x86-64-Qt-${{ matrix.qt-version }}.zip
working-directory: build
- name: Install InnoSetup
run: choco install innosetup
- name: Add InnoSetup to path
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.13.0
- name: Build installer
id: build-installer
working-directory: build
run: ..\.CI\build-installer.ps1
shell: powershell
- name: Upload installer
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

@ -1,28 +1,30 @@
name: 'Publish Homebrew Cask on Release'
name: "Publish Homebrew Cask on Release"
on:
push:
tags:
# Should match semver for mainline releases (not including -beta)
- 'v2.[0-9]+.[0-9]+'
- "v2.[0-9]+.[0-9]+"
# TODO: handle beta and nightly releases
# Need to make those casks manually first
# - v2.[0-9]+.[0-9]+-beta(?:[0-9]+)
env:
# This gets updated later on in the run by a bash script to strip the prefix
C2_CASK_NAME: 'chatterino'
C2_TAGGED_VERSION: '${{ github.ref }}'
C2_CASK_NAME: chatterino
# The full version of Chatterino (e.g. v2.4.1)
C2_TAGGED_VERSION: ${{ github.ref_name }}
HOMEBREW_GITHUB_API_TOKEN: ${{ secrets.HOMEBREW_GITHUB_API_TOKEN }}
jobs:
update_stable_homebrew_cask:
name: 'Update the stable homebrew cask'
runs-on: 'macos-latest'
name: "Update the stable homebrew cask"
runs-on: "macos-latest"
steps:
# Pulls out the version from the ref (e.g. refs/tags/v2.3.1 -> 2.3.1)
- name: 'Extract version from tag'
- name: Execute brew bump-cask-pr with version
run: |
'STRIPPED_VERSION=$(echo "refs/tags/$C2_TAGGED_VERSION" | sed "s/refs\/tags\/v//gm")'
'echo "C2_TAGGED_VERSION=$STRIPPED_VERSION" >> $GITHUB_ENV'
- name: 'Execute ''brew bump-cask-pr'' with version'
run: 'brew bump-cask-pr --version $C2_TAGGED_VERSION $C2_CASK_NAME'
echo "Running bump-cask-pr for cask '$C2_CASK_NAME' and version '$C2_TAGGED_VERSION'"
C2_TAGGED_VERSION_STRIPPED="${C2_TAGGED_VERSION:1}"
echo "Stripped version: '$C2_TAGGED_VERSION_STRIPPED'"
brew developer on
brew bump-cask-pr --version "$C2_TAGGED_VERSION_STRIPPED" "$C2_CASK_NAME"

View file

@ -6,8 +6,9 @@ on:
branches:
- master
pull_request:
merge_group:
concurrency:
concurrency:
group: lint-${{ github.ref }}
cancel-in-progress: true
@ -16,10 +17,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Lint Markdown files
uses: actionsx/prettier@v2
- name: Check formatting with Prettier
uses: actionsx/prettier@3d9f7c3fa44c9cb819e68292a328d7f4384be206
with:
# prettier CLI arguments.
args: --check '**/*.md'
args: --write .
- name: Show diff
run: git --no-pager diff --exit-code --color=never
shell: bash
- name: Check Theme files
run: |
npm i ajv-cli
npx -- ajv validate -s docs/ChatterinoTheme.schema.json -d "resources/themes/*.json"

View file

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

View file

@ -1,26 +0,0 @@
---
name: Build on Arch Linux
on:
push:
branches:
- master
pull_request:
concurrency:
group: build-archlinux-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Sync AUR package with current version
uses: pajlada/aur-sync-action@master
with:
package_name: chatterino2-git
commit_username: chatterino2-ci
commit_email: chatterino2-ci@pajlada.com
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
dry_run: true

98
.github/workflows/test-macos.yml vendored Normal file
View file

@ -0,0 +1,98 @@
---
name: Test MacOS
on:
pull_request:
workflow_dispatch:
merge_group:
env:
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 }}
cancel-in-progress: true
jobs:
test-macos:
name: "Test ${{ matrix.os }}, Qt ${{ matrix.qt-version }}"
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-13]
qt-version: [5.15.2, 6.7.1]
plugins: [false]
fail-fast: false
env:
C2_BUILD_WITH_QT6: ${{ startsWith(matrix.qt-version, '6.') && 'ON' || 'OFF' }}
QT_MODULES: ${{ startsWith(matrix.qt-version, '6.') && 'qt5compat qtimageformats' || '' }}
steps:
- name: Enable plugin support
if: matrix.plugins
run: |
echo "C2_PLUGINS=ON" >> "$GITHUB_ENV"
- name: Set BUILD_WITH_QT6
if: startsWith(matrix.qt-version, '6.')
run: |
echo "C2_BUILD_WITH_QT6=ON" >> "$GITHUB_ENV"
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0 # allows for tags access
- name: Install Qt
uses: jurplel/install-qt-action@v4.0.0
with:
cache: true
cache-key-prefix: ${{ runner.os }}-QtCache-${{ matrix.qt-version }}-v2
modules: ${{ env.QT_MODULES }}
version: ${{ matrix.qt-version }}
- name: Install dependencies
run: |
brew install boost openssl rapidjson p7zip create-dmg cmake
- name: Build
run: |
mkdir build-test
cd build-test
cmake \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DBUILD_TESTS=On \
-DBUILD_APP=OFF \
-DUSE_PRECOMPILED_HEADERS=OFF \
-DCHATTERINO_PLUGINS="$C2_PLUGINS" \
-DBUILD_WITH_QT6="$C2_BUILD_WITH_QT6" \
..
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: |
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

157
.github/workflows/test-windows.yml vendored Normal file
View file

@ -0,0 +1,157 @@
---
name: Test Windows
on:
pull_request:
workflow_dispatch:
merge_group:
env:
TWITCH_PUBSUB_SERVER_TAG: v1.0.7
QT_QPA_PLATFORM: minimal
# Last known good conan version
# 2.0.3 has a bug on Windows (conan-io/conan#13606)
CONAN_VERSION: 2.0.2
concurrency:
group: test-windows-${{ github.ref }}
cancel-in-progress: true
jobs:
test-windows:
name: "Test ${{ matrix.os }}, Qt ${{ matrix.qt-version }}"
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest]
qt-version: [5.15.2, 6.7.1]
plugins: [false]
skip-artifact: [false]
skip-crashpad: [false]
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_USE_OPENSSL3: ${{ startsWith(matrix.qt-version, '6.') && 'True' || 'False' }}
C2_CONAN_CACHE_SUFFIX: ${{ startsWith(matrix.qt-version, '6.') && '-QT6' || '' }}
steps:
- name: Enable plugin support
if: matrix.plugins
run: |
echo "C2_PLUGINS=ON" >> "$Env:GITHUB_ENV"
- name: Set Crashpad
if: matrix.skip-crashpad == false
run: |
echo "C2_ENABLE_CRASHPAD=ON" >> "$Env:GITHUB_ENV"
- name: Set BUILD_WITH_QT6
if: startsWith(matrix.qt-version, '6.')
run: |
echo "C2_BUILD_WITH_QT6=ON" >> "$Env:GITHUB_ENV"
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0 # allows for tags access
- name: Install Qt
uses: jurplel/install-qt-action@v4.0.0
with:
cache: true
cache-key-prefix: ${{ runner.os }}-QtCache-${{ matrix.qt-version }}-v2
modules: ${{ env.QT_MODULES }}
version: ${{ matrix.qt-version }}
- name: Enable Developer Command Prompt
uses: ilammy/msvc-dev-cmd@v1.13.0
- name: Setup sccache
# sccache v0.7.4
uses: hendrikmuhs/ccache-action@v1.2.14
with:
variant: sccache
# only save on the default (master) branch
save: ${{ github.event_name == 'push' }}
key: sccache-test-${{ matrix.os }}-${{ matrix.qt-version }}-${{ matrix.skip-crashpad }}
restore-keys: |
sccache-test-${{ matrix.os }}-${{ matrix.qt-version }}
- name: Cache conan packages
uses: actions/cache@v4
with:
key: ${{ runner.os }}-conan-user-${{ hashFiles('**/conanfile.py') }}${{ env.C2_CONAN_CACHE_SUFFIX }}
path: ~/.conan2/
- name: Install Conan
run: |
python3 -c "import site; import sys; print(f'{site.USER_BASE}\\Python{sys.version_info.major}{sys.version_info.minor}\\Scripts')" >> "$Env:GITHUB_PATH"
pip3 install --user "conan==${{ env.CONAN_VERSION }}"
- name: Setup Conan
run: |
conan --version
conan profile detect -f
- name: Install dependencies
run: |
mkdir build-test
cd build-test
conan install .. `
-s build_type=RelWithDebInfo `
-c tools.cmake.cmaketoolchain:generator="NMake Makefiles" `
-b missing `
--output-folder=. `
-o with_openssl3="$Env:C2_USE_OPENSSL3"
# The Windows runners currently use an older version of the CRT
- name: Install CRT
run: |
mkdir -Force build-test/bin
cp "$((ls $Env:VCToolsRedistDir/onecore/x64 -Filter '*.CRT')[0].FullName)/*" build-test/bin
- name: Build
run: |
cmake `
-G"NMake Makefiles" `
-DCMAKE_BUILD_TYPE=RelWithDebInfo `
-DBUILD_TESTS=On `
-DBUILD_APP=OFF `
-DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" `
-DUSE_PRECOMPILED_HEADERS=On `
-DBUILD_WITH_CRASHPAD="$Env:C2_ENABLE_CRASHPAD" `
-DCHATTERINO_PLUGINS="$Env:C2_PLUGINS" `
-DBUILD_WITH_QT6="$Env:C2_BUILD_WITH_QT6" `
..
set cl=/MP
nmake /S /NOLOGO
working-directory: build-test
- name: Download and extract Twitch PubSub Server Test
run: |
mkdir pubsub-server-test
Invoke-WebRequest -Uri "https://github.com/Chatterino/twitch-pubsub-server-test/releases/download/${{ env.TWITCH_PUBSUB_SERVER_TAG }}/server-${{ env.TWITCH_PUBSUB_SERVER_TAG }}-windows-amd64.zip" -outfile "pubsub-server.zip"
Expand-Archive pubsub-server.zip -DestinationPath pubsub-server-test
rm pubsub-server.zip
cd pubsub-server-test
Invoke-WebRequest -Uri "https://github.com/Chatterino/twitch-pubsub-server-test/raw/${{ env.TWITCH_PUBSUB_SERVER_TAG }}/cmd/server/server.crt" -outfile "server.crt"
Invoke-WebRequest -Uri "https://github.com/Chatterino/twitch-pubsub-server-test/raw/${{ env.TWITCH_PUBSUB_SERVER_TAG }}/cmd/server/server.key" -outfile "server.key"
cd ..
- name: Cargo Install httpbox
run: |
cargo install --git https://github.com/kevinastone/httpbox --rev 89b971f
- name: Test
timeout-minutes: 30
run: |
httpbox --port 9051 &
cd ..\pubsub-server-test
.\server.exe 127.0.0.1:9050 &
cd ..\build-test
ctest --repeat until-pass:4 --output-on-failure --exclude-regex ClassicEmoteNameFiltering
working-directory: build-test
- name: Clean Conan cache
run: conan cache clean --source --build --download "*"

View file

@ -1,90 +1,100 @@
---
name: Test
name: Test Ubuntu
on:
pull_request:
workflow_dispatch:
merge_group:
push:
branches:
- master
- main
env:
TWITCH_PUBSUB_SERVER_IMAGE: ghcr.io/chatterino/twitch-pubsub-server-test:v1.0.3
TWITCH_PUBSUB_SERVER_TAG: v1.0.7
QT_QPA_PLATFORM: minimal
concurrency:
concurrency:
group: test-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ${{ matrix.os }}
name: "${{ matrix.os }}"
runs-on: ubuntu-latest
container: ${{ matrix.container }}
strategy:
matrix:
os: [ubuntu-20.04]
qt-version: [5.15.2]
include:
- os: "ubuntu-22.04"
container: ghcr.io/chatterino/chatterino2-build-ubuntu-22.04:latest
qt-version: 6.7.1
plugins: true
fail-fast: false
env:
C2_PLUGINS: ${{ matrix.plugins }}
C2_BUILD_WITH_QT6: ${{ startsWith(matrix.qt-version, '6.') }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Cache Qt
id: cache-qt
uses: actions/cache@v3
with:
path: "${{ github.workspace }}/qt/"
key: ${{ runner.os }}-QtCache-${{ matrix.qt-version }}
# LINUX
- name: Install Qt
uses: jurplel/install-qt-action@v2
with:
cached: ${{ steps.cache-qt.outputs.cache-hit }}
version: ${{ matrix.qt-version }}
dir: "${{ github.workspace }}/qt/"
# LINUX
- name: Install dependencies (Ubuntu)
if: startsWith(matrix.os, 'ubuntu')
run: |
sudo apt-get update
sudo apt-get -y install \
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 dependencies
run: |
mkdir build-test
shell: bash
sudo apt update
sudo DEBIAN_FRONTEND=noninteractive apt -y --no-install-recommends install \
libbenchmark-dev gcovr gnupg
- name: Build (Ubuntu)
if: startsWith(matrix.os, 'ubuntu')
run: |
cmake -DBUILD_TESTS=On -DBUILD_APP=OFF ..
cmake --build . --config Release
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 \
-DCHATTERINO_GENERATE_COVERAGE=On \
-DCMAKE_BUILD_TYPE=Debug \
..
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: |
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
./bin/chatterino-test --platform minimal
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: |
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
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v4.5.0
with:
token: ${{ secrets.CODECOV_TOKEN }}
plugin: gcov
fail_ci_if_error: true
verbose: true

14
.github/workflows/winget.yml vendored Normal file
View file

@ -0,0 +1,14 @@
name: Publish to WinGet
on:
release:
types: [released]
jobs:
publish:
runs-on: windows-latest
if: ${{ startsWith(github.event.release.tag_name, 'v') }}
steps:
- uses: vedantmgoyal2009/winget-releaser@v2
with:
identifier: ChatterinoTeam.Chatterino
installers-regex: ^Chatterino.Installer.exe$
token: ${{ secrets.WINGET_TOKEN }}

14
.gitignore vendored
View file

@ -84,6 +84,7 @@ Thumbs.db
dependencies
.cache
.editorconfig
vim.log
### CMake ###
CMakeLists.txt.user
@ -111,3 +112,16 @@ Dependencies_*
# vcpkg
vcpkg_installed/
# NatVis files
qt5.natvis
qt6.natvis
# Autogenerated resource file
resources/resources_autogenerated.qrc
# Leftovers from running `aqt install`
aqtinstall.log
# sccache (CI)
.sccache

15
.gitmodules vendored
View file

@ -2,9 +2,6 @@
path = lib/libcommuni
url = https://github.com/Chatterino/libcommuni
branch = chatterino-cmake
[submodule "lib/qBreakpad"]
path = lib/qBreakpad
url = https://github.com/jiakuan/qBreakpad.git
[submodule "lib/WinToast"]
path = lib/WinToast
url = https://github.com/mohabouje/WinToast.git
@ -35,3 +32,15 @@
[submodule "lib/googletest"]
path = lib/googletest
url = https://github.com/google/googletest.git
[submodule "lib/miniaudio"]
path = lib/miniaudio
url = https://github.com/mackron/miniaudio.git
[submodule "lib/lua/src"]
path = lib/lua/src
url = https://github.com/lua/lua
[submodule "tools/crash-handler"]
path = tools/crash-handler
url = https://github.com/Chatterino/crash-handler
[submodule "lib/expected-lite"]
path = lib/expected-lite
url = https://github.com/martinmoene/expected-lite

View file

@ -0,0 +1,20 @@
This patch ensures Qt 5.15 in particular can build with modern compilers
See https://bugreports.qt.io/browse/QTBUG-91909 and https://codereview.qt-project.org/c/qt/qtbase/+/339417
---
diff --git a/src/concurrent/qtconcurrentthreadengine.h b/src/concurrent/qtconcurrentthreadengine.h
index cbd8ad04..4cd5b85 100644
--- a/src/concurrent/qtconcurrentthreadengine.h
+++ b/src/concurrent/qtconcurrentthreadengine.h
@@ -256,8 +256,8 @@
class ThreadEngineStarter<void> : public ThreadEngineStarterBase<void>
{
public:
- ThreadEngineStarter<void>(ThreadEngine<void> *_threadEngine)
- :ThreadEngineStarterBase<void>(_threadEngine) {}
+ ThreadEngineStarter(ThreadEngine<void> *_threadEngine)
+ : ThreadEngineStarterBase<void>(_threadEngine) {}
void startBlocking()
{

View file

@ -1,6 +1,31 @@
# JSON resources should not be prettified...
resources/*.json
benchmarks/resources/*.json
tests/resources/*.json
# ...themes should be prettified for readability.
!resources/themes/*.json
# Ignore submodule files
lib/*/
conan-pkgs/*/
cmake/sanitizers-cmake/
tools/crash-handler
.github/
# Build folders
*build-*/
[bB]uild
/_build/
# Editors
.vscode
.vs
.idea
dependencies
.cache
.editorconfig
# vcpkg
vcpkg_installed/
# Compile commands generated by CMake
compile_commands.json

6
.prettierrc Normal file
View file

@ -0,0 +1,6 @@
trailingComma: es5
endOfLine: auto
overrides:
- files: "*.md"
options:
proseWrap: preserve

View file

@ -1,6 +0,0 @@
trailingComma = "es5"
[[overrides]]
files = ["*.md"]
[overrides.options]
proseWrap = "preserve"

View file

@ -0,0 +1,2 @@
# Ignore openssl issues
interceptor_via_lib:libcrypto.so.3

View file

@ -0,0 +1,3 @@
# Ignore openssl issues
leak:libcrypto.so.3
leak:CRYPTO_zalloc

View file

@ -0,0 +1,17 @@
race:libdbus-1.so.3
deadlock:libdbus-1.so.3
race:libglib-2.0.so.0
race:libgio-2.0.so.0
# Not sure about these suppression
# race:qscopedpointer.h
# race:qarraydata.cpp
# race:qarraydata.h
# race:qarraydataops.h
# race:libQt6Core.so.6
# race:libQt6Gui.so.6
# race:libQt6XcbQpa.so.6
# race:libQt6Network.so.6
# very not sure about this one
# race:qstring.h

View file

@ -0,0 +1,2 @@
enum:NetworkResult.hpp
enum:gtest.h

View file

@ -1,23 +1,23 @@
# FreeBSD
Note on Qt version compatibility: If you are installing Qt from a package manager, please ensure the version you are installing is at least **Qt 5.12 or newer**.
For all dependencies below we use Qt 6. Our minimum supported version is Qt 5.15.2, but you are on your own.
## FreeBSD 12.1-RELEASE
## FreeBSD 14.0-RELEASE
Note: This is known to work on FreeBSD 12.1-RELEASE amd64. Chances are
Note: This is known to work on FreeBSD 14.0-RELEASE amd64. Chances are
high that this also works on older FreeBSD releases, architectures and
FreeBSD 13.0-CURRENT.
FreeBSD 15.0-SNAP.
1. Install build dependencies from package sources (or build from the
ports tree): `# pkg install qt5-core qt5-multimedia qt5-svg qt5-buildtools gstreamer-plugins-good boost-libs rapidjson cmake`
ports tree): `# pkg install boost-libs git qt6-base qt6-svg qt6-5compat qt6-imageformats qtkeychain-qt6 cmake`
1. In the project directory, create a build directory and enter it
```sh
mkdir build
cd build
```
1. Generate build files
1. Generate build files. To enable Lua plugins in your build add `-DCHATTERINO_PLUGINS=ON` to this command.
```sh
cmake ..
cmake -DBUILD_WITH_QT6=ON ..
```
1. Build the project
```sh

View file

@ -1,38 +1,49 @@
# Linux
Note on Qt version compatibility: If you are installing Qt from a package manager, please ensure the version you are installing is at least **Qt 5.12 or newer**.
For all dependencies below we use Qt 6. Our minimum supported version is Qt 5.15.2, but you are on your own.
## Install dependencies
### Ubuntu 20.04
### Ubuntu
_Most likely works the same for other Debian-like distros_
Building on Ubuntu requires Docker.
Install all of the dependencies using `sudo apt install qttools5-dev qtmultimedia5-dev libqt5svg5-dev libboost-dev libssl-dev libboost-system-dev libboost-filesystem-dev cmake g++`
Use <https://github.com/Chatterino/docker/pkgs/container/chatterino2-build-ubuntu-20.04> as your base if you're on Ubuntu 20.04.
Use <https://github.com/Chatterino/docker/pkgs/container/chatterino2-build-ubuntu-22.04> if you're on Ubuntu 22.04.
The built binary should be exportable from the final image & able to run on your system assuming you perform a static build. See our [build.yml GitHub workflow file](.github/workflows/build.yml) for the CMake line used for Ubuntu builds.
### Debian 12 (bookworm) or later
```sh
sudo apt install qt6-base-dev qt6-5compat-dev qt6-svg-dev qt6-image-formats-plugins libboost1.81-dev libssl-dev cmake g++ git
```
### Arch Linux
Install all of the dependencies using `sudo pacman -S --needed qt5-base qt5-multimedia qt5-svg qt5-tools gst-plugins-ugly gst-plugins-good boost rapidjson pkgconf openssl cmake`
```sh
sudo pacman -S --needed qt6-base qt6-tools boost-libs openssl qt6-imageformats qt6-5compat qt6-svg 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.
### Fedora 28 and above
### Fedora 39 and above
_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-qtmultimedia-devel qt5-qtsvg-devel qt5-linguist libsecret-devel openssl-devel boost-devel cmake`
```sh
sudo dnf install qt6-qtbase-devel qt6-qtimageformats qt6-qtsvg-devel qt6-qt5compat-devel g++ git 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`
```sh
nix-shell -p openssl boost qt6.full pkg-config cmake
```
## Compile
### Through Qt Creator
1. Install C++ IDE Qt Creator by using `sudo apt install qtcreator`
1. Open `CMakeLists.txt` with Qt Creator and select build
## Manually
1. In the project directory, create a build directory and enter it
@ -40,11 +51,16 @@ Enter the development environment with all of the dependencies: `nix-shell -p op
mkdir build
cd build
```
1. Generate build files
1. Generate build files. To enable Lua plugins in your build add `-DCHATTERINO_PLUGINS=ON` to this command.
```sh
cmake ..
cmake -DBUILD_WITH_QT6=ON -DBUILD_WITH_QTKEYCHAIN=OFF ..
```
1. Build the project
```sh
make
cmake --build .
```
### Through Qt Creator
1. Install C++ IDE Qt Creator by using `sudo apt install qtcreator` (Or whatever equivalent for your distro)
1. Open `CMakeLists.txt` with Qt Creator and select build

View file

@ -1,32 +1,32 @@
# Building on macOS
#### Note - If you want to develop Chatterino 2 you might also want to install Qt Creator (make sure to install **Qt 5.12 or newer**), it is not required though and any C++ IDE (might require additional setup for cmake to find Qt libraries) or a normal text editor + running cmake from terminal should work as well
Chatterino2 is built in CI on Intel on macOS 13.
Local dev machines for testing are available on Apple Silicon on macOS 13.
#### Note - Chatterino 2 is only tested on macOS 10.14 and above - anything below that is considered unsupported. It may or may not work on earlier versions
## Installing dependencies
1. Install Xcode and Xcode Command Line Utilities
1. Start Xcode, go into Settings -> Locations, and activate your Command Line Tools
1. Install brew https://brew.sh/
1. Install the dependencies using `brew install boost openssl rapidjson cmake`
1. Install Qt5 using `brew install qt@5`
1. (_OPTIONAL_) Install [ccache](https://ccache.dev) (used to speed up compilation by using cached results from previous builds) using `brew install ccache`
1. Go into the project directory
1. Create a build folder and go into it (`mkdir build && cd build`)
1. Compile using `cmake .. && make`
1. Install [Homebrew](https://brew.sh/#install)
We use this for dependency management on macOS
1. Install all dependencies:
`brew install boost openssl@1.1 rapidjson cmake qt@5`
If the Project does not build at this point, you might need to add additional Paths/Libs, because brew does not install openssl and boost in the common path. You can get their path using
## Building
`brew info openssl`
`brew info boost`
### Building from terminal
If brew doesn't link OpenSSL properly then you should be able to link it yourself by using these two commands:
1. Open a terminal
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. To enable Lua plugins in your build add `-DCHATTERINO_PLUGINS=ON` to this command.
`cmake -DCMAKE_PREFIX_PATH=/opt/homebrew/opt/qt@5 -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl@1.1 ..`
1. Build:
`make`
- `ln -s /usr/local/opt/openssl/lib/* /usr/local/lib`
- `ln -s /usr/local/opt/openssl/include/openssl /usr/local/include/openssl`
Your binary can now be found under bin/chatterino.app/Contents/MacOS/chatterino directory
The lines which you need to add to your project file should look similar to this
### Other building methods
```
INCLUDEPATH += /usr/local/opt/openssl/include
LIBS += -L/usr/local/opt/openssl/lib
```
You can achieve similar results by using an IDE like Qt Creator, although this is undocumented but if you know the IDE you should have no problems applying the terminal instructions to your IDE.

View file

@ -1,140 +1,191 @@
# Building on Windows
**Note that installing all of the development prerequisites and libraries will require about 30 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.
## Visual Studio 2022
## Prerequisites
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".
### Visual Studio
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 17 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.
## Boost
### Qt
1. Visit the [Qt Open Source Page](https://www.qt.io/download-open-source).
2. Scroll down to the bottom
3. Then select "Download the Qt Online Installer"
4. Within the installer, Qt will prompt you to create an account to access Qt downloads.
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.15.2 or later**.
#### 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`)
3. Under this version, select the following entries:
- `MSVC 2019 64-bit` (or `MSVC 2022 64-bit` from Qt 6.8 onwards)
- `Qt 5 Compatibility Module`
- `Additional Libraries` > `Qt Image Formats`
4. Under the "Tools" tree element (at the bottom), ensure that `Qt Creator X.X.X` and `Debugging Tools for Windows` are selected. (they should be checked by default)
5. Continue through the installer and let the installer finish installing Qt.
Note: This installation will take about 2 GB of disk space.
Once Qt is done installing, make sure you add its bin directory to your `PATH` (e.g. `C:\Qt\6.5.3\msvc2019_64\bin`)
<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.
4. Click the `New` button top right and paste in the file path for your Qt installation (e.g. `C:\Qt\6.5.3\msvc2019_64\bin` by default).
5. Click `Ok`
</details>
### Advanced dependencies
These dependencies are only required if you are not using a package manager
<details>
<summary>Boost</summary>
1. First, download a boost installer appropriate for your version of Visual Studio.
- Visit the downloads list on [SourceForge](https://sourceforge.net/projects/boost/files/boost-binaries/).
- Select the latest version from the list.
- Download the `.exe` file appropriate to your Visual Studio installation version and system bitness (choose `-64` for 64-bit systems).
Visual Studio versions map as follows: `14.3` in the filename corresponds to MSVC 2022,`14.2` to 2019, `14.1` to 2017, `14.0` to 2015. _Anything prior to Visual Studio 2015 is unsupported. Please upgrade should you have an older installation._
Visual Studio versions map as follows: `14.3` in the filename corresponds to MSVC 2022. _Anything prior to Visual Studio 2022 is unsupported. Please upgrade should you have an older installation._
**Convenience link for Visual Studio 2022: [boost_1_79_0-msvc-14.3-64.exe](https://sourceforge.net/projects/boost/files/boost-binaries/1.79.0/boost_1_79_0-msvc-14.3-64.exe/download)**
**Convenience link for Visual Studio 2022: [boost_1_84_0-msvc-14.3-64.exe](https://sourceforge.net/projects/boost/files/boost-binaries/1.84.0/boost_1_84_0-msvc-14.3-64.exe/download)**
2. When prompted where to install Boost, set the location to `C:\local\boost`.
3. After the installation finishes, rename the `C:\local\boost\lib64-msvc-14.3` (or similar) directory to simply `lib` (`C:\local\boost\lib`).
Note: This installation will take about 2.1 GB of disk space.
## OpenSSL
</details>
### For our websocket library, we need OpenSSL 1.1
## Building
1. Download OpenSSL for windows, version `1.1.1q`: **[Download](https://slproweb.com/download/Win64OpenSSL-1_1_1q.exe)**
2. When prompted, install OpenSSL to `C:\local\openssl`
3. When prompted, copy the OpenSSL DLLs to "The OpenSSL binaries (/bin) directory".
### Using CMake
### For Qt SSL, we need OpenSSL 1.0
#### Install conan 2
1. Download OpenSSL for Windows, version `1.0.2u`: **[Download](https://web.archive.org/web/20211109231823/https://slproweb.com/download/Win64OpenSSL-1_0_2u.exe)**
2. When prompted, install it to any arbitrary empty directory.
3. When prompted, copy the OpenSSL DLLs to "The OpenSSL binaries (/bin) directory".
4. Copy the OpenSSL 1.0 files from its `\bin` folder to `C:\local\bin` (You will need to create the folder)
5. Then copy the OpenSSL 1.1 files from its `\bin` folder to `C:\local\bin` (Overwrite any duplicate files)
6. Add `C:\local\bin` to your path folder ([Follow the guide here if you don't know how to do it](https://www.computerhope.com/issues/ch000549.htm#windows10))
Install [conan 2](https://conan.io/downloads.html) and make sure it's in your `PATH` (default setting).
**If the 1.1.x download link above does not work, try downloading the similar 1.1.x version found [here](https://slproweb.com/products/Win32OpenSSL.html). Note: Don't download the "light" installer, it does not have the required files.**
![Screenshot Slproweb layout](https://user-images.githubusercontent.com/41973452/175827529-97802939-5549-4ab1-95c4-d39f012d06e9.png)
<details>
<summary>Adding conan to your PATH if you installed it with pip</summary>
Note: This installation will take about 200 MB of disk space.
_Note: This will add all Python-scripts to your `PATH`, conan being one of them._
## Qt
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 `System variables` section, scroll down until you find `Path` and double click it.
4. Click the `New` button top right.
5. Open up a terminal `where.exe conan` to find the file path (the folder that contains the conan.exe) to add.
6. Add conan 2's file path (e.g. `C:\Users\example\AppData\Roaming\Python\Python311\Scripts`) to the blank text box that shows up. This is your current Python installation's scripts folder.
7. Click `Ok`
1. Visit the [Qt Open Source Page](https://www.qt.io/download-open-source).
2. Scroll down to the bottom
3. Then select "Download the Qt Online Installer"
</details>
Notes:
Then in a terminal, configure conan to use `NMake Makefiles` as its generator:
- 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**.
1. Generate a new profile
`conan profile detect`
### When prompted which components to install:
#### Build
1. Unfold the tree element that says "Qt"
2. Unfold the top most tree element (latest stable Qt version, e.g. `Qt 5.15.2`)
3. Under this version, select the following entries:
- `MSVC 2019 64-bit` (or alternative version if you are using that)
- `Qt WebEngine` (optional)
4. Under the "Tools" tree element (at the bottom), ensure that `Qt Creator X.X.X` and `Debugging Tools for Windows` are selected. (they should be checked by default)
5. Continue through the installer and let the installer finish installing Qt.
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:
Note: This installation will take about 2 GB of disk space.
```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
```
## Compile with Breakpad support (Optional)
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)
To build with plugins add `-DCHATTERINO_PLUGINS=ON` to `cmake` command.
Compiling with Breakpad support enables crash reports that can be of use for developing/beta versions of Chatterino. If you have no interest in reporting crashes anyways, this optional dependency will probably be of no use to you.
#### Deploying Qt libraries
1. Open up `lib/qBreakpad/handler/handler.pro`in Qt Creator
2. Build it in whichever mode you want to build Chatterino in (Debug/Profile/Release)
3. Copy the newly built `qBreakpad.lib` to the following directory: `lib/qBreakpad/build/handler` (You will have to manually create this directory)
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/`
## Run the build in Qt Creator
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`
### 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_5_15_2_MSVC2019_64bit-Release`.)
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
#### 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\5.15.2\msvc2019_64\bin\windeployqt.exe`.
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_5_15_2_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_5_15_2_MSVC2019_64bit-Release\release
C:\Qt\5.15.2\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. Go to `C:\local\bin\` and copy these dll's into your `release folder`.
#### Formatting
libssl-1_1-x64.dll
libcrypto-1_1-x64.dll
ssleay32.dll
libeay32.dll
To automatically format your code, do the following:
6. The `releases` directory will now be populated with all the required files to make the chatterino build standalone.
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`
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)).
### Building on MSVC with AddressSanitizer
## Using CMake
Make sure you installed `C++ AddressSanitizer` in your VisualStudio installation like described in the [Microsoft Docs](https://learn.microsoft.com/en-us/cpp/sanitizers/asan#install-the-addresssanitizer).
Open up your terminal with the Visual Studio environment variables, then enter the following commands:
To build Chatterino with AddressSanitizer on MSVC, you need to add `-DCMAKE_CXX_FLAGS=/fsanitize=address` to your CMake options.
1. `mkdir build`
2. `cd build`
3. `cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DUSE_CONAN=ON ..`
4. `nmake`
When you start Chatterino, and it's complaining about `clang_rt.asan_dbg_dynamic-x86_64.dll` missing,
copy the file found in `<VisualStudio-installation-path>\VC\Tools\MSVC\<version>\bin\Hostx64\x64\clang_rt.asan_dbg_dynamic-x86_64.dll` to the `Chatterino` folder inside your `build` folder.
## Building/Running in CLion
To learn more about AddressSanitizer and MSVC, visit the [Microsoft Docs](https://learn.microsoft.com/en-us/cpp/sanitizers/asan).
### Developing in CLion
_Note:_ We're using `build` instead of the CLion default `cmake-build-debug` folder.
@ -147,10 +198,9 @@ 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
```
-DCMAKE_PREFIX_PATH=C:\Qt\5.15.2\msvc2019_64\lib\cmake\Qt5
-DUSE_CONAN=ON
-DCMAKE_CXX_FLAGS=/bigobj
```text
-DCMAKE_PREFIX_PATH=C:\Qt\6.5.3\msvc2019_64\lib\cmake\Qt6
-DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake"
```
and the _Build Directory_ to `build`.
@ -166,7 +216,7 @@ After the CMake project is loaded, open the _Run/Debug Configurations_.
Select the `CMake Applications > chatterino` configuration and add a new _Run External tool_ task to _Before launch_.
- Set the _Program_ to `C:\Qt\5.15.2\msvc2019_64\bin\windeployqt.exe`
- Set the _Program_ to `C:\Qt\6.5.3\msvc2019_64\bin\windeployqt.exe`
- Set the _Arguments_
to `$CMakeCurrentProductFile$ --debug --no-compiler-runtime --no-translations --no-opengl-sw --dir bin/`
- Set the _Working directory_ to `$ProjectFileDir$\build`
@ -179,13 +229,37 @@ 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>
Now you can run the `chatterino | Debug` configuration.
If you want to run the portable version of Chatterino, create a file called `modes` inside of `build/bin` and
If you want to run the portable version of Chatterino, create a file called `modes` inside `build/bin` and
write `portable` into it.
#### Debugging
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
the project root using PowerShell:
<!--
We can't use Invoke-RestMethod here, because it will automatically convert the body to an xml document.
-->
```powershell
(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
(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.
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

@ -1,35 +1,54 @@
# Building on Windows with vcpkg
This will require more than 30 GB of free space on your hard drive.
## Prerequisites
1. Install [Visual Studio](https://visualstudio.microsoft.com/) with "Desktop development with C++" (~9.66 GB)
1. Install [CMake](https://cmake.org/) (~109 MB)
1. Install [git](https://git-scm.com/) (~264 MB)
1. Install [vcpkg](https://vcpkg.io/) (~80 MB)
- `git clone https://github.com/Microsoft/vcpkg.git`
- `cd .\vcpkg\`
- `.\bootstrap-vcpkg.bat`
- `.\vcpkg integrate install`
- `.\vcpkg integrate powershell`
- `cd ..`
1. Configure the environment for vcpkg
- `set VCPKG_DEFAULT_TRIPLET=x64-windows`
- [default](https://github.com/microsoft/vcpkg/blob/master/docs/users/triplets.md#additional-remarks) is `x86-windows`
- `set VCPKG_ROOT=C:\path\to\vcpkg\`
- `set PATH=%PATH%;%VCPKG_ROOT%`
1. Install [Visual Studio](https://visualstudio.microsoft.com/) with "Desktop development with C++"
1. Install [CMake](https://cmake.org/)
1. Install [git](https://git-scm.com/)
1. Install [vcpkg](https://vcpkg.io/)
```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
e.g. `setx VCPKG_DEFAULT_TRIPLET x64-windows`
See [documentation about Triplets](https://learn.microsoft.com/en-gb/vcpkg/users/triplets)
[default](https://github.com/microsoft/vcpkg/blob/master/docs/users/triplets.md#additional-remarks) is `x86-windows`
- Set VCPKG_ROOT to the vcpkg path
e.g. `setx VCPKG_ROOT <path to vcpkg>`
See [VCPKG_ROOT documentation](https://learn.microsoft.com/en-gb/vcpkg/users/config-environment#vcpkg_root)
- Append the vcpkg path to your path
e.g. `setx PATH "%PATH%;<path to vcpkg>"`
- For more configurations, see <https://learn.microsoft.com/en-gb/vcpkg/users/config-environment>
1. You may need to restart your computer to ensure all your environment variables and what-not are loaded everywhere.
## Building
1. Clone
- `git clone --recurse-submodules https://github.com/Chatterino/chatterino2.git`
1. Install dependencies (~21 GB)
- `cd .\chatterino2\`
- `vcpkg install`
```shell
git clone --recurse-submodules https://github.com/Chatterino/chatterino2.git
```
1. Install dependencies
```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.
To build with plugins add `-DCHATTERINO_PLUGINS=ON` to `cmake -B build` command.
1. Run `.\bin\chatterino2.exe`

View file

@ -2,35 +2,612 @@
## Unversioned
- Major: Added support for Twitch's Chat Replies. [Wiki Page](https://wiki.chatterino.com/Features/#message-replies) (#3722)
- Major: Release plugins alpha. (#5288)
- Major: Improve high-DPI support on Windows. (#4868, #5391)
- Minor: Removed the Ctrl+Shift+L hotkey for toggling the "live only" tab visibility state. (#5530)
- Minor: Moved tab visibility control to a submenu, without any toggle actions. (#5530)
- Minor: Add option to customise Moderation buttons with images. (#5369)
- Minor: Colored usernames now update on the fly when changing the "Color @usernames" setting. (#5300)
- Minor: Added `flags.action` filter variable, allowing you to filter on `/me` messages. (#5397)
- Minor: Added the ability for `/ban`, `/timeout`, `/unban`, and `/untimeout` to specify multiple channels to duplicate the action to. Example: `/timeout --channel id:11148817 --channel testaccount_420 forsen 7m game complaining`. (#5402)
- Minor: The size of the emote popup is now saved. (#5415)
- Minor: Added the ability to duplicate tabs. (#5277)
- Minor: Improved error messages for channel update commands. (#5429)
- Minor: Moderators can now see when users are warned. (#5441)
- Minor: Added support for Brave & google-chrome-stable browsers. (#5452)
- Minor: Added drop indicator line while dragging in tables. (#5256)
- Minor: Add channel points indication for new bits power-up redemptions. (#5471)
- Minor: Added option to log streams by their ID, allowing for easier "per-stream" log analyzing. (#5507)
- Minor: Added `/warn <username> <reason>` command for mods. This prevents the user from chatting until they acknowledge the warning. (#5474)
- Minor: Added option to suppress live notifictions on startup. (#5388)
- Minor: Improve appearance of reply button. (#5491)
- Minor: Introduce HTTP API for plugins. (#5383, #5492, #5494)
- Minor: Support more Firefox variants for incognito link opening. (#5503)
- Minor: Replying to a message will now display the message being replied to. (#4350, #5519)
- Minor: Links can now have prefixes and suffixes such as parentheses. (#5486, #5515)
- Minor: Added support for scrolling in splits with touchscreen panning gestures. (#5524)
- Minor: Removed experimental IRC support. (#5547)
- Minor: Moderators can now see which mods start and cancel raids. (#5563)
- Bugfix: Fixed tab move animation occasionally failing to start after closing a tab. (#5426)
- Bugfix: If a network request errors with 200 OK, Qt's error code is now reported instead of the HTTP status. (#5378)
- Bugfix: Fixed restricted users usernames not being clickable. (#5405)
- Bugfix: Fixed a crash that could occur when logging was enabled in IRC servers that were removed. (#5419)
- Bugfix: Fixed message history occasionally not loading after a sleep. (#5457)
- Bugfix: Fixed a crash when tab completing while having an invalid plugin loaded. (#5401)
- Bugfix: Fixed windows on Windows not saving correctly when snapping them to the edges. (#5478)
- Bugfix: Fixed user info card popups adding duplicate line to log files. (#5499)
- Bugfix: Fixed tooltips and input completion popups not working after moving a split. (#5541, #5576)
- Bugfix: Fixed rare issue on shutdown where the client would hang. (#5557)
- Bugfix: Fixed `/clearmessages` not working with more than one window. (#5489)
- Bugfix: Fixed splits staying paused after unfocusing Chatterino in certain configurations. (#5504)
- Bugfix: Links with invalid characters in the domain are no longer detected. (#5509)
- Bugfix: Fixed janky selection for messages with RTL segments (selection is still wrong, but consistently wrong). (#5525)
- Bugfix: Fixed tab visibility being controllable in the emote popup. (#5530)
- Bugfix: Fixed account switch not being saved if no other settings were changed. (#5558)
- Bugfix: Fixed some tooltips not being readable. (#5578)
- Dev: Update Windows build from Qt 6.5.0 to Qt 6.7.1. (#5420)
- Dev: Update vcpkg build Qt from 6.5.0 to 6.7.0, boost from 1.83.0 to 1.85.0, openssl from 3.1.3 to 3.3.0. (#5422)
- Dev: Unsingletonize `ISoundController`. (#5462)
- Dev: Use Qt's high DPI scaling. (#4868, #5400)
- Dev: Add doxygen build target. (#5377)
- Dev: Make printing of strings in tests easier. (#5379)
- Dev: Refactor and document `Scrollbar`. (#5334, #5393)
- Dev: Refactor `TwitchIrcServer`, making it abstracted. (#5421, #5435)
- Dev: Reduced the amount of scale events. (#5404, #5406)
- Dev: Removed unused timegate settings. (#5361)
- Dev: Add `Channel::addSystemMessage` helper function, allowing us to avoid the common `channel->addMessage(makeSystemMessage(...));` pattern. (#5500)
- Dev: Unsingletonize `Resources2`. (#5460)
- Dev: All Lua globals now show in the `c2` global in the LuaLS metadata. (#5385)
- Dev: Images are now loaded in worker threads. (#5431)
- Dev: Fixed broken `SignalVector::operator[]` implementation. (#5556)
- Dev: Qt Creator now auto-configures Conan when loading the project and skips vcpkg. (#5305)
- Dev: The MSVC CRT is now bundled with Chatterino as it depends on having a recent version installed. (#5447)
- Dev: Refactor/unsingletonize `UserDataController`. (#5459)
- Dev: Cleanup `BrowserExtension`. (#5465)
- Dev: Deprecate Qt 5.12. (#5396)
- Dev: Refactored `MessageFlag` into its own file. (#5549)
- Dev: The running Qt version is now shown in the about page if it differs from the compiled version. (#5501)
- Dev: `FlagsEnum` is now `constexpr`. (#5510)
- Dev: Documented and added tests to RTL handling. (#5473)
- Dev: Refactored 7TV/BTTV definitions out of `TwitchIrcServer` into `Application`. (#5532)
- Dev: Refactored code that's responsible for deleting old update files. (#5535)
- Dev: Cleanly exit on shutdown. (#5537)
- Dev: Removed the `getTwitchAbstract` method in `Application`. (#5560)
- Dev: Renamed threads created by Chatterino on Linux and Windows. (#5538, #5539, #5544)
- Dev: Refactored a few `#define`s into `const(expr)` and cleaned includes. (#5527)
- Dev: Added `FlagsEnum::isEmpty`. (#5550)
- Dev: Prepared for Qt 6.8 by addressing some deprecations. (#5529)
- Dev: Moved some responsibility away from Application into WindowManager. (#5551)
- Dev: Fixed benchmarks segfaulting on run. (#5559)
- Dev: Refactored `MessageBuilder` to be a single class. (#5548)
- Dev: Recent changes are now shown in the nightly release description. (#5553, #5554)
- Dev: The timer for `StreamerMode` is now destroyed on the correct thread. (#5571)
## 2.5.1
- Bugfix: Fixed links without a protocol not being clickable. (#5345)
## 2.5.0
- Major: Twitch follower emotes can now be correctly tabbed in other channels when you are subscribed to the channel the emote is from. (#4922)
- Major: Added `/automod` split to track automod caught messages across all open channels the user moderates. (#4986, #5026)
- Major: Moderators can now see restricted chat messages and suspicious treatment updates. (#5056, #5060)
- Minor: Migrated to the new Get Channel Followers Helix endpoint, fixing follower count not showing up in usercards. (#4809)
- Minor: Moderation commands such as `/ban`, `/timeout`, `/unban`, and `/untimeout` can now be used via User IDs by using the `id:123` syntax (e.g. `/timeout id:22484632 1m stop winning`). (#4945, #4956, #4957)
- Minor: The `/usercard` command now accepts user ids. (`/usercard id:22484632`) (#4934)
- Minor: Added menu actions to reply directly to a message or the original thread root. (#4923)
- Minor: The `/reply` command now replies to the latest message from the user. Due to this change, the message you intended to reply to is now shown in the reply context, instead of the first message in a thread. (#4919)
- Minor: The chatter list button is now hidden if you don't have moderator privileges. (#5245)
- Minor: Live streams that are marked as reruns now mark a tab as yellow instead of red. (#5176, #5237)
- Minor: Allowed theming of tab live and rerun indicators. (#5188)
- Minor: The _Restart on crash_ setting works again on Windows. (#5012)
- Minor: Added an option to use new experimental smarter emote completion. (#4987)
- Minor: Added support for FrankerFaceZ channel badges. These can be configured at https://www.frankerfacez.com/channel/mine - currently only supports bot badges for your chat bots. (#5119)
- Minor: Added support to send /announce[color] commands. Colored announcements only appear with the chosen color in Twitch chat. (#5250)
- 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 a warning message if you have multiple commands with the same trigger. (#4322)
- Minor: Chatters from message history are now added to autocompletion. (#5116)
- Minor: Added support for the `{input.text}` placeholder in the **Split** -> **Run a command** hotkey. (#5130)
- Minor: Added `--activate <channel>` (or `-a`) command line option to focus or add a certain Twitch channel on startup. (#5111)
- 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 the ability to change the top-most status of a window regardless of the _Always on top_ setting (right click the notebook). (#5135)
- 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: Updated to Emoji v15.1. Google emojis are now used as the fallback instead of Twitter emojis. (#5182)
- Minor: Added icons for newer versions of macOS. (#5148)
- Minor: Added more menu items in macOS menu bar. (#5266)
- Minor: Improved color selection and display. (#5057)
- Minor: Added a _System_ theme setting that updates according to the system's color scheme (requires Qt 6.5). (#5118)
- Minor: Normalized the input padding between light & dark themes. (#5095)
- Minor: The account switcher is now styled to match your theme. (#4817)
- 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: Added a new completion API for experimental plugins feature. (#5000, #5047)
- Minor: Added a new Channel API for experimental plugins feature. (#5141, #5184, #5187)
- Minor: Introduce `c2.later()` function to Lua API. (#5154)
- Minor: Added `--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 wrappers for Lua `io` library for experimental plugins feature. (#5231)
- Minor: Added permissions to experimental plugins feature. (#5231)
- Minor: Added missing periods at various moderator messages and commands. (#5061)
- Minor: Improved Streamlink documentation in the settings dialog. (#5076)
- Minor: The installer now checks for the VC Runtime version and shows more info when it's outdated. (#4847)
- Minor: All sound capabilities can now be disabled by setting your "Sound backend" setting to "Null" and restarting Chatterino. (#4978)
- Minor: Added an invisible resize handle to the bottom of frameless user info popups and reply thread popups. (#4795)
- Minor: Updated the flatpakref link included with nightly builds to point to up-to-date flathub-beta builds. (#5008)
- 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 `reward.cost` `reward.id`, `reward.title` filter variables. (#5275)
- Minor: Change Lua `CompletionRequested` handler to use an event table. (#5280)
- Minor: Changed the layout of the about page. (#5287)
- Minor: Add duration to multi-month anon sub gift messages. (#5293)
- Minor: Added context menu action to toggle visibility of offline tabs. (#5318)
- Minor: Report sub duration for more multi-month gift cases. (#5319)
- Minor: Improved error reporting for the automatic streamer mode detection on Linux and macOS. (#5321)
- Bugfix: Fixed a crash that could occur on Wayland when using the image uploader. (#5314)
- Bugfix: Fixed split tooltip getting stuck in some cases. (#5309)
- Bugfix: Fixed the version string not showing up as expected in Finder on macOS. (#5311)
- Bugfix: Fixed links having `http://` added to the beginning in certain cases. (#5323)
- Bugfix: Fixed topmost windows from losing their status after opening dialogs on Windows. (#5330)
- Bugfix: Fixed a gap appearing when using filters on `/watching`. (#5329)
- Bugfix: Removed the remnant "Show chatter list" menu entry for non-moderators. (#5336)
- Bugfix: Fixed an issue where certain emojis did not send to Twitch chat correctly. (#4840)
- Bugfix: Fixed the `/shoutout` command not working with usernames starting with @'s (e.g. `/shoutout @forsen`). (#4800)
- 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)
- Bugfix: Fixed a performance issue when displaying replies to certain messages. (#4807)
- Bugfix: Fixed an issue where certain parts of the split input wouldn't focus the split when clicked. (#4958)
- Bugfix: Fixed an issue in the `/live` split that caused some channels to not get grayed-out when they went offline. (#5172)\
- Bugfix: User text input within watch streak notices now correctly shows up. (#5029)
- Bugfix: Fixed selection of tabs after closing a tab when using "Live Tabs Only". (#4770)
- Bugfix: Fixed input in the reply thread popup losing focus when dragging said window. (#4815)
- Bugfix: Fixed the Quick Switcher (CTRL+K) 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 issue on Windows preventing the title bar from being dragged in the top left corner. (#4873)
- Bugfix: Fixed an issue where Streamer Mode did not detect that OBS was running on MacOS. (#5260)
- Bugfix: Remove ":" from the message the user is replying to if it's a /me message. (#5263)
- Bugfix: Fixed the "Cancel" button in the settings dialog only working after opening the settings dialog twice. (#5229)
- 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 an empty page being added when showing the out of bounds dialog. (#4849)
- Bugfix: Fixed an issue preventing searching a redemption by it's title when the redemption contained user text input. (#5117)
- 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 sometimes 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 tooltips appearing too large and/or away from the cursor. (#4920)
- 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 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 double-click selection not correctly selecting words that were split onto multiple lines. (#5243)
- Bugfix: Fixed some emotes not appearing when using _Ignores_. (#4965, #5126)
- Bugfix: Fixed a freeze from a bad regex in _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 a rare crash with the 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: The usercard button is now hidden 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 clicking `More messages below` button in a usercard and closing it quickly. (#4933)
- 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: Fixed avatar in usercard and moderation button triggering when releasing the mouse outside their area. (#5052)
- 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: Fixed some Twitch emotes sizes being wrong at certain zoom levels. (#5279, #5291)
- Bugfix: Fixed a missing space when the image uploader provided a delete link. (#5269)
- 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 split header tooltips showing in the wrong position on Windows. (#5230)
- Bugfix: Fixed split header tooltips appearing too tall. (#5232)
- Bugfix: Fixed past messages not showing in the search popup after adding a channel. (#5248)
- Bugfix: Fixed pause indicator not disappearing in some cases. (#5265)
- Bugfix: Fixed the usercard popup not floating on tiling WMs on Linux when "Automatically close user popup when it loses focus" setting is enabled. (#3511)
- Bugfix: Fixed moderator-only topics being subscribed to for non-moderators. (#5056)
- Bugfix: Truncated IRC messages to be at most 512 bytes. (#5246)
- Bugfix: Fixed a data race when disconnecting from Twitch PubSub. (#4771)
- Bugfix: Fixed messages not immediately disappearing when clearing the chat. (#5282)
- Bugfix: Fixed highlights triggering for ignored users in announcements. (#5295)
- Dev: Changed the order of the query parameters for Twitch player URLs. (#5326)
- 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)
- Dev: Refactor `Image` & Image's `Frames`. (#4773)
- Dev: Add `WindowManager::getLastSelectedWindow()` to replace `getMainWindow()`. (#4816)
- Dev: Clarify signal connection lifetimes where applicable. (#4818)
- Dev: Laid the groundwork for advanced input completion strategies. (#4639, #4846, #4853, #4893)
- Dev: Fixed flickering when running with Direct2D on Windows. (#4851)
- Dev: Fix qtkeychain include for Qt6 users. (#4863)
- Dev: Add a compile-time flag `CHATTERINO_UPDATER` which can be turned off to disable update checks. (#4854)
- Dev: Add a compile-time flag `USE_SYSTEM_MINIAUDIO` which can be turned on to use the system miniaudio. (#4867)
- Dev: Update vcpkg to use Qt6. (#4872)
- Dev: Update `magic_enum` to v0.9.5. (#4992)
- 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, #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: Compile Lua as a C library. (#5251)
- 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, #5283)
- 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, #5276)
- 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)
- Dev: Adapted `magic_enum` to Qt's Utf-16 strings. (#5258)
- Dev: `NetworkManager`'s statics are now created in its `init` method. (#5254, #5297)
- Dev: `clang-tidy` CI now uses Qt 6. (#5273)
- Dev: Enabled `InsertNewlineAtEOF` in `clang-format`. (#5278)
## 2.4.6
- Minor: Migrate to the new Get Channel Followers Helix endpoint, fixing follower count not showing up in usercards. (#4809)
- Bugfix: Update Qt version, fixing a security issue with webp loading (see https://www.qt.io/blog/two-qt-security-advisorys-gdi-font-engine-webp-image-format) (#4843)
- Dev: Temporarily disable High DPI scaling on Qt6 builds on Windows. (#4767)
## 2.4.5
- Major: AutoMod term management messages (e.g. testaccount added "noob" as a blocked term on AutoMod.) are now hidden in Streamer Mode if you have the "Hide moderation actions" setting enabled. (#4758)
- Minor: Added `/shoutout <username>` command to shoutout a specified user. Note: This is only the /command, we are still unable to display when a shoutout happens. (#4638)
- Minor: Added a setting to only show tabs with live channels (default toggle hotkey: Ctrl+Shift+L). (#4358)
- Minor: Added an option to subscribe to and unsubscribe from reply threads. (#4680, #4739)
- Minor: Added the ability to pin Reply threads to stay open while using the setting "Automatically close reply thread popup when it loses focus". (#4680)
- Minor: Highlights loaded from message history will now correctly appear in the /mentions tab. (#4475)
- Minor: Added hotkey Action for pinning usercards and reply threads. (#4692)
- Minor: Added missing hotkey Action for Open Player in Browser. (#4756)
- Minor: Added an icon showing when streamer mode is enabled (#4410, #4690)
- Minor: Message input is now focused when clicking on emotes. (#4719)
- Minor: Changed viewer list to chatter list to more match Twitch's terminology. (#4732)
- Minor: Added currency & duration to Hype Chat messages. (#4715)
- Minor: Added `is:hype-chat` search option. (#4766)
- Minor: Added `flags.hype_chat` filter variable. (#4766)
- Minor: Nicknames are now taken into consideration when searching for messages. (#4663, #4742)
- Minor: Added a message for when Chatterino joins a channel (#4616)
- Minor: 7TV badges now automatically update upon changing them. (#4512)
- Minor: Removed restriction on Go To Message on system messages from search. (#4614)
- Minor: Channel point redemptions without custom text are now shown in the usercard. (#4557)
- Minor: Added settings for customizing the behavior of `Right Click`ing a usernames. (#4622, #4751)
- Minor: The input completion and quick switcher are now styled to match your theme. (#4671)
- Minor: All channels opened in browser tabs are synced when using the extension for quicker switching between tabs. (#4741)
- Minor: Added support for opening incognito links in firefox-esr and chromium. (#4745)
- Minor: Added support for opening incognito links under Linux/BSD using XDG. (#4745)
- Minor: Added accelerators to the right click menu for messages (#4705)
- Minor: Improved editing hotkeys. (#4628)
- Minor: Added `/c2-theme-autoreload` command to automatically reload a custom theme. This is useful for when you're developing your own theme. (#4718)
- Bugfix: Fixed an issue where Subscriptions & Announcements that contained ignored phrases would still appear if the Block option was enabled. (#4748)
- Bugfix: Increased amount of blocked users loaded from 100 to 1,000. (#4721)
- Bugfix: Fixed pings firing for the "Your username" highlight when not signed in. (#4698)
- Bugfix: Fixed a crash that could happen when closing splits before their display name was updated. This was especially noticeable after the live controller changes. (#4731)
- Bugfix: Fixed highlights sometimes not working after changing sound device, or switching users in your operating system. (#4729)
- Bugfix: Fixed a spacing issue with mentions inside RTL text. (#4677)
- Bugfix: Fixed a crash when opening and closing a reply thread and switching the user. (#4675)
- Bugfix: Fixed a crash that could occur when closing the usercard too quickly after blocking or unblocking a user. (#4711)
- Bugfix: Fixed visual glitches with smooth scrolling. (#4501)
- Bugfix: Fixed key bindings not showing in context menus on Mac. (#4722)
- Bugfix: Fixed timeouts from message history not behaving consistently. (#4760)
- Bugfix: Fixed partially broken filters on Qt 6 builds. (#4702)
- Bugfix: Fixed tooltips & popups sometimes showing up on the wrong monitor. (#4740)
- Bugfix: Fixed some network errors having `0` as their HTTP status. (#4704)
- Bugfix: Fixed tab completion rarely completing the wrong word. (#4735)
- Bugfix: Fixed generation of crashdumps by the browser-extension process when the browser was closed. (#4667)
- Dev: Stream status requests are now batched. (#4713)
- Dev: Added command to set Qt's logging filter/rules at runtime (`/c2-set-logging-rules`). (#4637)
- Dev: Added the ability to see & load custom themes from the Themes directory. No stable promises are made of this feature, changes might be made that breaks custom themes without notice. (#4570)
- Dev: Added test cases for emote and tab completion. (#4644)
- Dev: Fixed `clang-tidy-review` action not picking up dependencies. (#4648)
- Dev: Expanded upon `$$$` test channels. (#4655)
- Dev: Added tools to help debug image GC. (#4578)
- Dev: Removed duplicate license when having plugins enabled. (#4665)
- Dev: Replace our QObjectRef class with Qt's QPointer class. (#4666)
- Dev: Fixed warnings about QWidgets already having a QLayout. (#4672)
- Dev: Fixed undefined behavior when loading non-existent credentials. (#4673)
- Dev: Small refactor of the recent-messages API, splitting its internal API and its internal implementation up into separate files. (#4763)
- Dev: Added support for compiling with `sccache`. (#4678)
- Dev: Added `sccache` in Windows CI. (#4678)
- Dev: Moved preprocessor Git and date definitions to executables only. (#4681)
- Dev: Refactored tests to be able to use `ctest` and run in debug builds. (#4700)
- Dev: Added the ability to use an alternate linker using the `-DUSE_ALTERNATE_LINKER=...` CMake parameter. (#4711)
- Dev: The Windows installer is now built in CI. (#4408)
- Dev: Removed `getApp` and `getSettings` calls from message rendering. (#4535)
- Dev: Get the default browser executable instead of the entire command line when opening incognito links. (#4745)
- Dev: Removed unused code hidden behind the USEWEBENGINE define (#4757)
## 2.4.4
- Minor: Added a Send button in the input box so you can click to send a message. This is disabled by default and can be enabled with the "Show send message button" setting. (#4607)
- Minor: Improved error messages when the updater fails a download. (#4594)
- Minor: Added `/shield` and `/shieldoff` commands to toggle shield mode. (#4580)
- Bugfix: Fixed the menu warping on macOS on Qt6. (#4595)
- Bugfix: Fixed link tooltips not showing unless the thumbnail setting was enabled. (#4597)
- Bugfix: Domains starting with `http` are now parsed as links again. (#4598)
- Bugfix: Reduced the size of the update prompt to prevent it from going off the users screen. (#4626)
- Bugfix: Fixed click effects on buttons not being antialiased. (#4473)
- Bugfix: Fixed Ctrl+Backspace not working after Select All in chat search popup. (#4461)
- Bugfix: Fixed crash when scrolling up really fast. (#4621)
- Dev: Added the ability to control the `followRedirect` mode for requests. (#4594)
## 2.4.3
- Major: Added support for FrankerFaceZ animated emotes. (#4434)
- Minor: Added the ability to reply to a message by `Shift + Right Click`ing the username. (#4424)
- Minor: Reply context now censors blocked users. (#4502)
- Minor: Migrated the viewer list to Helix API. (#4117)
- Minor: Migrated badges to Helix API. (#4537)
- Minor: Added `/lowtrust` command to open the suspicious user activity feed in browser. (#4542)
- Minor: Added better filter validation and error messages. (#4364)
- Minor: Updated the look of the Black Theme to be more in line with the other themes. (#4523)
- Minor: Re-added leading @mentions from replies in chat logs. These were accidentally removed during the reply overhaul. (#4420)
- Minor: Added a local backup of the Twitch Badges API in case the request fails. (#4463)
- Minor: Updated the macOS icon to be consistent with the design of other applications on macOS. (#4577)
- Bugfix: Fixed an issue where Chatterino could lose track of the sound device in certain scenarios. (#4549)
- Bugfix: Fixed an issue where animated emotes would render on top of zero-width emotes. (#4314)
- Bugfix: Fixed an issue where it was difficult to hover a zero-width emote. (#4314)
- Bugfix: Fixed an issue where context-menu items for zero-width emotes displayed the wrong provider. (#4460)
- Bugfix: Fixed an issue where the "Enable zero-width emotes" setting was showing the inverse state. (#4462)
- Bugfix: Fixed blocked user list being empty when opening the settings dialog for the first time. (#4437)
- Bugfix: Fixed blocked user list sticking around when switching from a logged in user to being logged out. (#4437)
- Bugfix: Fixed search popup ignoring setting for message scrollback limit. (#4496)
- Bugfix: Fixed a memory leak that occurred when loading message history. This was mostly noticeable with unstable internet connections where reconnections were frequent or long-running instances of Chatterino. (#4499)
- Bugfix: Fixed Twitch channel-specific filters not being applied correctly. (#4529)
- Bugfix: Fixed `/mods` displaying incorrectly when the channel has no mods. (#4546)
- Bugfix: Fixed emote & badge tooltips not showing up when thumbnails were hidden. (#4509)
- Bugfix: Fixed links with invalid IPv4 addresses being parsed. (#4576)
- Bugfix: Fixed the macOS icon changing to the wrong icon when the application is open. (#4577)
- Dev: Disabling precompiled headers on Windows is now tested in CI. (#4472)
- Dev: Themes are now stored as JSON files in `resources/themes`. (#4471, #4533)
- Dev: Ignore unhandled BTTV user-events. (#4438)
- Dev: Only log debug messages when NDEBUG is not defined. (#4442)
- Dev: Cleaned up theme related code. (#4450)
- Dev: Ensure tests have default-initialized settings. (#4498)
- Dev: Add scripting capabilities with Lua (#4341, #4504)
- Dev: Conan 2.0 is now used instead of Conan 1.0. (#4417)
- Dev: Added tests and benchmarks for `LinkParser`. (#4436)
- Dev: Removed redundant parsing of links. (#4507)
- Dev: Experimental builds with Qt 6 are now provided. (#4522, #4551, #4553, #4554, #4555, #4556)
- Dev: Fixed username rendering in Qt 6. (#4476, #4568)
- Dev: Fixed placeholder color in Qt 6. (#4477)
- Dev: Removed `CHATTERINO_TEST` definitions. (#4526)
- Dev: Builds for macOS now have `macos` in their name (previously: `osx`). (#4550)
- Dev: Fixed a crash when dragging rows in table-views in builds with Qt 6. (#4567)
## 2.4.2
- Minor: Added `/banid` command that allows banning by user ID. (#4411)
- Bugfix: Fixed FrankerFaceZ emotes/badges not loading due to an API change. (#4432)
- Bugfix: Fixed uploaded AppImage not being able to execute most web requests. (#4400)
- Bugfix: Fixed a potential race condition due to using the wrong lock when loading 7TV badges. (#4402)
- Dev: Delete all but the last 5 crashdumps on application start. (#4392)
- Dev: Added capability to build Chatterino with Qt6. (#4393)
- Dev: Fixed homebrew update action. (#4394)
## 2.4.1
- Major: Added live emote updates for BTTV. (#4147)
- Minor: Added setting to turn off rendering of reply context. (#4224)
- Minor: Changed the highlight order to prioritize Message highlights over User highlights. (#4303)
- Minor: Added a setting to highlight your own messages in `Highlights -> Users`. (#3833)
- Minor: Added the ability to negate search options by prefixing it with an exclamation mark (e.g. `!badge:mod` to search for messages where the author does not have the moderator badge). (#4207)
- Minor: Search window input will automatically use currently selected text if present. (#4178)
- Minor: Grouped highlight sound columns together and improved wording for the default sound setting. (#4194)
- Minor: Tables in settings window will now scroll to newly added rows. (#4216)
- Minor: Added setting to select which channels to log. (#4302)
- Minor: Added channel name to /mentions log entries. (#4371)
- Minor: Added link to streamlink docs for easier user setup. (#4217)
- Minor: Added support for HTTP and Socks5 proxies through environment variables. (#4321)
- Minor: Added crashpad to capture crashes on Windows locally. See PR for build/crash analysis instructions. (#4351)
- Minor: Github releases now include flatpakref files for nightly builds
- Bugfix: Fixed User Card moderation actions not working after Twitch IRC chat command deprecation. (#4378)
- Bugfix: Fixed User Card broadcaster actions (mod, unmod, vip, unvip) not working after Twitch IRC chat command deprecation. (#4387)
- Bugfix: Fixed crash that would occur when performing certain actions after removing all tabs. (#4271)
- Bugfix: Fixed highlight sounds not reloading on change properly. (#4194)
- Bugfix: Fixed CTRL + C not working in reply thread popups. (#4209)
- Bugfix: Fixed message input showing as red after removing a message that was more than 500 characters. (#4204)
- Bugfix: Fixed unnecessary saving of windows layout. (#4201)
- Bugfix: Fixed Reply window missing selection clear behaviour between chat and input box. (#4218)
- Bugfix: Fixed crash that could occur when changing Tab layout and utilizing multiple windows. (#4248)
- Bugfix: Fixed text sometimes not pasting properly when image uploader was disabled. (#4246)
- Bugfix: Fixed text cursor(caret) not showing in open channel dialog. (#4196)
- Bugfix: Fixed tooltip images not appearing if mouse hovered only first pixel. (#4268)
- Bugfix: Fixed crash that could occur when closing down a split at the wrong time. (#4277)
- Bugfix: Fixed crash that could occur when closing down the last of a channel when reloading emotes. (#4278)
- Bugfix: Fixed scrollbar highlight colors when changing message history limit. (#4288)
- Bugfix: Fixed the split "Search" menu action not opening the correct search window. (#4305)
- Bugfix: Fixed an issue on Windows when opening links in incognito mode that contained forward slashes in hash (#4307)
- Bugfix: Fixed an issue where beta versions wouldn't update to stable versions correctly. (#4329)
- Bugfix: Fixed builds from GitHub showing up as modified. (#4384)
- Bugfix: Avoided crash that could occur when receiving channel point reward information. (#4360)
- Dev: Changed sound backend from Qt to miniaudio. (#4334)
- Dev: Removed sending part of the multipart emoji workaround. (#4361)
- Dev: Removed protocol from QApplication's Organization Domain (so changed from `https://www.chatterino.com` to `chatterino.com`). (#4256)
- Dev: Ignore `WM_SHOWWINDOW` hide events, causing fewer attempted rescales. (#4198)
- Dev: Migrated to C++ 20 (#4252, #4257)
- Dev: Enable LTO for main branch builds. (#4258, #4260)
- Dev: Removed unused include directives. (#4266, #4275, #4294)
- Dev: Removed TooltipPreviewImage. (#4268)
- Dev: Removed unused operators in `Image` (#4267)
- Dev: Removed usage of deprecated `QDesktopWidget` (#4287)
- Dev: Bump Cirrus CI FreeBSD image from 12.1 to 13.1. (#4295)
- Dev: Fixed `inconsistent-missing-override` warnings. (#4296)
- Dev: Fixed `final-dtor-non-final-class` warnings. (#4296)
- Dev: Fixed `ambiguous-reversed-operator` warnings. (#4296)
- Dev: Format YAML and JSON files with prettier. (#4304)
- Dev: Added CMake Install Support on Windows. (#4300)
- Dev: Changed conan generator to [`CMakeDeps`](https://docs.conan.io/en/latest/reference/conanfile/tools/cmake/cmakedeps.html) and [`CMakeToolchain`](https://docs.conan.io/en/latest/reference/conanfile/tools/cmake/cmaketoolchain.html). See PR for migration notes. (#4335)
- Dev: Refactored 7TV EventAPI implementation. (#4342)
- Dev: Disabled ImageExpirationPool in tests. (#4363)
- Dev: Don't rely on undocumented registry keys to find the default browser on Windows. (#4362)
- Dev: Use `QEnterEvent` for `QWidget::enterEvent` on Qt 6. (#4365)
- Dev: Use `qintptr` in `QWidget::nativeEvent` on Qt 6. (#4376)
## 2.4.0
- Major: Added support for emotes, badges, and live emote updates from [7TV](https://7tv.app). [Wiki Page](https://wiki.chatterino.com/Third_party_services/#7tv) (#4002, #4062, #4090)
- Major: Added support for Right-to-Left Languages (#3958, #4139, #4168)
- Major: Added support for Twitch's Chat Replies. [Wiki Page](https://wiki.chatterino.com/Features/#message-replies) (#3722, #3989, #4041, #4047, #4055, #4067, #4077, #3905, #4131)
- Major: Added multi-channel searching to search dialog via keyboard shortcut. (Ctrl+Shift+F by default) (#3694, #3875)
- Minor: Added option to display tabs on the right and bottom. (#3847)
- Minor: Added `is:first-msg` search option. (#3700)
- Minor: Added quotation marks in the permitted/blocked Automod messages for clarity. (#3654)
- Minor: Added a `Scroll to top` keyboard shortcut for splits. (#3802)
- Minor: Adjust large stream thumbnail to 16:9 (#3655)
- Minor: Fixed being unable to load Twitch Usercards from the `/mentions` tab. (#3623)
- Minor: Add information about the user's operating system in the About page. (#3663)
- Minor: Added chatter count for each category in viewer list. (#3683, #3719)
- Minor: Sorted usernames in /vips message to be case-insensitive. (#3696)
- Minor: Strip leading @ and trailing , from usernames in the `/block` and `/unblock` commands. (#3816)
- Minor: Added option to open a user's chat in a new tab from the usercard profile picture context menu. (#3625)
- Minor: Fixed tag parsing for consecutive escaped characters. (#3711)
- Minor: Prevent user from entering incorrect characters in Live Notifications channels list. (#3715, #3730)
- Minor: Fixed automod caught message notice appearing twice for mods. (#3717)
- Minor: Streamer mode now automatically detects if XSplit, PRISM Live Studio, Twitch Studio, or vMix are running. (#3740)
- Minor: Add scrollbar to `Select filters` dialog. (#3737)
- Minor: Added `/requests` command. Usage: `/requests [channel]`. Opens the channel points requests queue for the provided channel or the current channel if no input is provided. (#3746)
- Minor: Added ability to execute commands on chat messages using the message context menu. (#3738, #3765)
- Minor: Added `/copy` command. Usage: `/copy <text>`. Copies provided text to clipboard - can be useful with custom commands. (#3763)
- Minor: Added setting to keep more message history in splits. (#3811)
- Minor: Added setting to keep more message history in usercards. (#3811)
- Minor: Added ability to pin Usercards to stay open even if it loses focus. Only available if "Automatically close usercard when it loses focus" is enabled. (#3884)
- Minor: Allow hiding moderation actions in streamer mode. (#3926)
- Minor: Added highlights for `Elevated Messages`. (#4016)
- Minor: Removed total views from the usercard, as Twitch no longer updates the number. (#3792)
- Minor: Add Quick Switcher item to open a channel in a new popup window. (#3828)
- Minor: Warn when parsing an environment variable fails. (#3904)
- Minor: Load missing messages from Recent Messages API upon reconnecting (#3878, #3932)
- Minor: Add settings to toggle BTTV/FFZ global/channel emotes (#3935)
- Bugfix: Fix crash that can occur when closing and quickly reopening a split, then running a command. (#3852)
- Minor: Reduced image memory usage when running Chatterino for a long time. (#3915)
- Minor: Added the ability to execute commands on chat messages using the message context menu. (#3738, #3765)
- Minor: Added settings to toggle BTTV/FFZ global/channel emotes (#3935, #3990)
- Minor: Added an option to display tabs on the right and bottom. (#3847)
- Minor: Added a `Scroll to top` keyboard shortcut for splits. (#3802)
- Minor: Added `/copy` command. Usage: `/copy <text>`. Copies provided text to clipboard - can be useful with custom commands. (#3763)
- Minor: Added `/requests` command. Usage: `/requests [channel]`. Opens the channel points requests queue for the provided channel or the current channel if no input is provided. (#3746)
- Minor: Added `Go to message` context menu action to search popup, mentions, usercard and reply threads. (#3953)
- Minor: Clicking `A message from x was deleted` messages will now jump to the message in question. (#3953)
- Minor: Added `is:first-msg` search option. (#3700)
- Minor: Added `is:elevated-msg` search option. (#4018)
- Minor: Added `is:cheer-msg` search option. (#4069)
- Minor: Added `is:redemption` search option. (#4118)
- Minor: Added `is:reply` search option. (#4119)
- Minor: Added `subtier:` search option (e.g. `subtier:3` to find Tier 3 subs). (#4013)
- Minor: Added `badge:` search option (e.g. `badge:mod` to users with the moderator badge). (#4013)
- Minor: Added AutoMod message flag filter. (#3938)
- Minor: Added `showInMentions` toggle for Badge Highlights. (#4034)
- Minor: Added chatter count for each category in viewer list. (#3683, #3719)
- Minor: Added option to open a user's chat in a new tab from the usercard profile picture context menu. (#3625)
- Minor: Added scrollbar to `Select filters` dialog. (#3737)
- Minor: Added quotation marks in the permitted/blocked Automod messages for clarity. (#3654)
- Minor: Added Quick Switcher item to open a channel in a new popup window. (#3828)
- Minor: Added information about the user's operating system in the About page. (#3663)
- Minor: Added option to hide inline whispers in streamer mode (#4076)
- Minor: Adjusted large stream thumbnail to 16:9 (#3655)
- Minor: Prevented user from entering incorrect characters in Live Notifications channels list. (#3715, #3730)
- Minor: Sorted usernames in /vips message to be case-insensitive. (#3696)
- Minor: Streamer mode now automatically detects if XSplit, PRISM Live Studio, Twitch Studio, or vMix are running. (#3740)
- Minor: Fixed automod caught message notice appearing twice for mods. (#3717)
- Minor: Fixed being unable to load Twitch Usercards from the `/mentions` tab. (#3623)
- Minor: Strip leading @ and trailing , from usernames in the `/block` and `/unblock` commands. (#3816)
- Minor: Fixed tag parsing for consecutive escaped characters. (#3711)
- Minor: Reduced GIF frame window from 30ms to 20ms, causing fewer frame skips in animated emotes. (#3886, #3907)
- Minor: Warn when parsing an environment variable fails. (#3904)
- Minor: Migrated /announce command to Helix API. (#4003)
- Minor: Migrated /clear command to Helix API. (#3994)
- Minor: Migrated /color command to Helix API. (#3988)
- Minor: Migrated /delete command to Helix API. (#3999)
- Minor: Migrated /emoteonly command to Helix API. (#4015)
- Minor: Migrated /emoteonlyoff command to Helix API. (#4015)
- Minor: Migrated /mod command to Helix API. (#4000)
- Minor: Migrated /unmod command to Helix API. (#4001)
- Minor: Migrated /vip command to Helix API. (#4010)
- Minor: Migrated /unvip command to Helix API. (#4025)
- Minor: Migrated /untimeout to Helix API. (#4026)
- Minor: Migrated /unban to Helix API. (#4026, #4050)
- Minor: Migrated /subscribers to Helix API. (#4040)
- Minor: Migrated /subscribersoff to Helix API. (#4040)
- Minor: Migrated /slow to Helix API. (#4040)
- Minor: Migrated /slowoff to Helix API. (#4040)
- Minor: Migrated /followers to Helix API. (#4040)
- Minor: Migrated /followersoff to Helix API. (#4040)
- Minor: Migrated /raid command to Helix API. Chat command will continue to be used until February 11th 2023. (#4029)
- Minor: Migrated /unraid command to Helix API. Chat command will continue to be used until February 11th 2023. (#4030)
- Minor: Migrated /ban to Helix API. (#4049, #4164)
- Minor: Migrated /timeout to Helix API. (#4049, #4164)
- Minor: Migrated /w to Helix API. Chat command will continue to be used until February 11th 2023. (#4052)
- Minor: Migrated /vips to Helix API. Chat command will continue to be used until February 11th 2023. (#4053)
- Minor: Migrated /uniquechat and /r9kbeta to Helix API. (#4057)
- Minor: Migrated /uniquechatoff and /r9kbetaoff to Helix API. (#4057)
- Minor: Migrated /commercial to Helix API. (#4094, #4141)
- Minor: Added stream titles to windows live toast notifications. (#1297)
- Minor: Make menus and placeholders display appropriate custom key combos. (#4045)
- Minor: Migrated /chatters to Helix API. (#4088, #4097, #4114)
- Minor: Migrated /mods to Helix API. (#4103)
- Minor: Improved text selection to match Windows native behaviour. (#4127)
- Minor: Add settings tooltips. (#3437)
- Minor: Add setting to limit message input length. (#3418)
- Minor: Make built-in commands work in IRC channels. (#4160)
- Minor: Add support for `echo-message` capabilities for IRC. (#4157)
- Minor: Add proper support for IRC private messages. (#4158)
- Minor: Improved look of tabs when using a layout other than top. (#3925, #4152)
- Minor: Added support for Nicknames on IRC. (#4170)
- Bugfix: Fixed crash happening when QuickSwitcher is used with a popout window. (#4187)
- Bugfix: Fixed low contrast of text in settings tooltips. (#4188)
- Bugfix: Fixed being unable to see the usercard of VIPs who have Asian language display names. (#4174)
- Bugfix: Fixed whispers always being shown in the /mentions split. (#4389)
- Bugfix: Fixed messages where Right-to-Left order is mixed in multiple lines. (#4173)
- Bugfix: Fixed the wrong right-click menu showing in the chat input box. (#4177)
- Bugfix: Fixed popup windows not appearing/minimizing correctly on the Windows taskbar. (#4181)
- Bugfix: Fixed white border appearing around maximized window on Windows. (#4190)
- Bugfix: Fixed window scaling being applied too many times on startup, causing windows like Settings to be slow. (#4193)
- Bugfix: Fixed input text cursor flickering when selecting text in a split. (#4197)
- Bugfix: Fixed shipped resources having incorrect ICC profile (#4199)
- Bugfix: Fixed channels with two leading `#`s not being usable on IRC (#4154)
- Bugfix: Fixed `Add new account` dialog causing main chatterino window to be non movable. (#4121)
- Bugfix: Connection to Twitch PubSub now recovers more reliably. (#3643, #3716)
- Bugfix: Fix crash that can occur when changing channels. (#3799)
- Bugfix: Fixed `Smooth scrolling on new messages` setting sometimes hiding messages. (#4028)
- Bugfix: Fixed context menu not opening when username is right clicked from usercard/search/reply window. (#4122)
- Bugfix: Fixed a crash that can occur when closing and quickly reopening a split, then running a command. (#3852)
- Bugfix: Fixed a crash that can occur when changing channels. (#3799)
- Bugfix: Fixed viewers list search not working when used before loading finishes. (#3774)
- Bugfix: Fixed live notifications for usernames containing uppercase characters. (#3646)
- Bugfix: Fixed live notifications not getting updated for closed streams going offline. (#3678)
@ -41,21 +618,37 @@
- Bugfix: Fixed existing emote popups not being raised from behind other windows when refocusing them on macOS (#3713)
- Bugfix: Fixed automod queue pubsub topic persisting after user change. (#3718)
- Bugfix: Fixed viewer list not closing after pressing escape key. (#3734)
- Bugfix: Fixed users being assigned duplicate FrankerFaceZ badges. (#4155)
- Bugfix: Fixed links with no thumbnail having previous link's thumbnail. (#3720)
- Bugfix: Fixed message only showing a maximum of one global FrankerFaceZ badge even if the user has multiple. (#3818)
- Bugfix: Add icon in the CMake macOS bundle. (#3832)
- Bugfix: Adopt popup windows in order to force floating behavior on some window managers. (#3836)
- Bugfix: Fix split focusing being broken in certain circumstances when the "Show input when it's empty" setting was disabled. (#3838, #3860)
- Bugfix: Added an icon in the CMake macOS bundle. (#3832)
- Bugfix: Adopted popup windows in order to force floating behavior on some window managers. (#3836)
- Bugfix: Fixed split focusing being broken in certain circumstances when the "Show input when it's empty" setting was disabled. (#3838, #3860)
- Bugfix: Always refresh tab when a contained split's channel is set. (#3849)
- Bugfix: Drop trailing whitespace from Twitch system messages. (#3888)
- Bugfix: Fix crash related to logging IRC channels (#3918)
- Bugfix: Fixed an issue where Anonymous gift messages appeared larger than normal gift messages. (#3888)
- Bugfix: Fixed crash related to logging IRC channels (#3918)
- Bugfix: Mentions of "You" in timeouts will link to your own user now instead of the user "You". (#3922)
- Dev: Remove official support for QMake. (#3839, #3883)
- Dev: Rewrite LimitedQueue (#3798)
- Dev: Overhaul highlight system by moving all checks into a Controller allowing for easier tests. (#3399, #3801, #3835)
- Bugfix: Fixed emoji popup not being shown in IRC channels (#4021)
- Bugfix: Display sent IRC messages like received ones (#4027)
- Bugfix: Fixed non-global FrankerFaceZ emotes from being loaded as global emotes. (#3921)
- Bugfix: Fixed trailing spaces from preventing Nicknames from working correctly. (#3946)
- Bugfix: Fixed crashes that can occur while selecting/copying messages and they are removed. (#4153)
- Bugfix: Fixed trailing spaces from preventing User Highlights from working correctly. (#4051)
- Bugfix: Fixed channel-based popups from rewriting messages to file log (#4060)
- Bugfix: Fixed invalid/dangling completion when cycling through previous messages or replying (#4072)
- Bugfix: Fixed incorrect .desktop icon path. (#4078)
- Bugfix: Mark bad or invalid images as empty. (#4151)
- Bugfix: Fixed `/watching` channel jumping around. (#4169)
- Dev: Got rid of BaseTheme (#4132)
- Dev: Removed official support for QMake. (#3839, #3883)
- Dev: Rewrote LimitedQueue (#3798)
- Dev: Set cmake `QT_DISABLE_DEPRECATED_BEFORE` to disable deprecated APIs up to Qt 5.15.0 (#4133)
- Dev: Overhauled highlight system by moving all checks into a Controller allowing for easier tests. (#3399, #3801, #3835)
- Dev: Use Game Name returned by Get Streams instead of querying it from the Get Games API. (#3662)
- Dev: Batch checking live status for all channels after startup. (#3757, #3762, #3767)
- Dev: Move most command context into the command controller. (#3824)
- Dev: Batched checking live status for all channels after startup. (#3757, #3762, #3767)
- Dev: Moved most command context into the command controller. (#3824)
- Dev: Error NetworkResults now include the body data. (#3987)
- Dev: Automatically generate resources files with cmake. (#4159, #4167)
## 2.3.5
@ -82,6 +675,8 @@
- Minor: Fixed `/streamlink` command not stripping leading @'s or #'s (#3215)
- Minor: Strip leading @ and trailing , from username in `/popout` command. (#3217)
- Minor: Added `flags.reward_message` filter variable (#3231)
- Minor: Added `flags.elevated_message` filter variable. (#4017)
- Minor: Added `flags.cheer_message` filter variable. (#4069)
- Minor: Added chatter count to viewer list popout (#3261)
- Minor: Ignore out of bounds check for tiling wms (#3270)
- Minor: Add clear cache button to cache settings section (#3277)

View file

@ -1,5 +1,6 @@
cmake_minimum_required(VERSION 3.8)
cmake_policy(SET CMP0087 NEW)
cmake_minimum_required(VERSION 3.15)
cmake_policy(SET CMP0087 NEW) # evaluates generator expressions in `install(CODE/SCRIPT)`
cmake_policy(SET CMP0091 NEW) # select MSVC runtime library through `CMAKE_MSVC_RUNTIME_LIBRARY`
include(FeatureSummary)
list(APPEND CMAKE_MODULE_PATH
@ -7,8 +8,6 @@ list(APPEND CMAKE_MODULE_PATH
"${CMAKE_SOURCE_DIR}/cmake/sanitizers-cmake/cmake"
)
project(chatterino VERSION 2.3.5)
option(BUILD_APP "Build Chatterino" ON)
option(BUILD_TESTS "Build the tests for Chatterino" OFF)
option(BUILD_BENCHMARKS "Build the benchmarks for Chatterino" OFF)
@ -16,10 +15,44 @@ option(USE_SYSTEM_PAJLADA_SETTINGS "Use system pajlada settings library" OFF)
option(USE_SYSTEM_LIBCOMMUNI "Use system communi library" OFF)
option(USE_SYSTEM_QTKEYCHAIN "Use system QtKeychain library" OFF)
option(BUILD_WITH_QTKEYCHAIN "Build Chatterino with support for your system key chain" ON)
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)
option(BUILD_SHARED_LIBS "" OFF)
option(CHATTERINO_LTO "Enable LTO for all targets" OFF)
option(CHATTERINO_PLUGINS "Enable ALPHA plugin support in Chatterino" OFF)
option(USE_CONAN "Use conan" OFF)
option(CHATTERINO_UPDATER "Enable update checks" ON)
mark_as_advanced(CHATTERINO_UPDATER)
if(BUILD_TESTS)
list(APPEND VCPKG_MANIFEST_FEATURES "tests")
endif()
if(BUILD_BENCHMARKS)
list(APPEND VCPKG_MANIFEST_FEATURES "benchmarks")
endif()
project(chatterino
VERSION 2.5.1
DESCRIPTION "Chat client for twitch.tv"
HOMEPAGE_URL "https://chatterino.com/"
)
if(CHATTERINO_LTO)
include(CheckIPOSupported)
check_ipo_supported(RESULT CHATTERINO_ENABLE_LTO OUTPUT IPO_ERROR)
message(STATUS "LTO: Enabled (Supported: ${CHATTERINO_ENABLE_LTO} - ${IPO_ERROR})")
else()
message(STATUS "LTO: Disabled")
endif()
if (BUILD_WITH_QT6)
set(MAJOR_QT_VERSION "6")
@ -27,18 +60,51 @@ else()
set(MAJOR_QT_VERSION "5")
endif()
if (USE_CONAN OR CONAN_EXPORTED)
include(${CMAKE_CURRENT_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup(TARGETS NO_OUTPUT_DIRS)
else ()
set(QT_CREATOR_SKIP_CONAN_SETUP ON)
find_program(CCACHE_PROGRAM ccache)
find_program(SCCACHE_PROGRAM sccache)
if (SCCACHE_PROGRAM)
set(_compiler_launcher ${SCCACHE_PROGRAM})
elseif (CCACHE_PROGRAM)
set(_compiler_launcher ${CCACHE_PROGRAM})
endif ()
# Alternate linker code taken from heavyai/heavydb
# https://github.com/heavyai/heavydb/blob/0517d99b467806f6af7b4c969e351368a667497d/CMakeLists.txt#L87-L103
macro(set_alternate_linker linker)
find_program(LINKER_EXECUTABLE ld.${USE_ALTERNATE_LINKER} ${USE_ALTERNATE_LINKER})
if(LINKER_EXECUTABLE)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND "${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 12.0.0)
add_link_options("-ld-path=${USE_ALTERNATE_LINKER}")
else()
add_link_options("-fuse-ld=${USE_ALTERNATE_LINKER}")
endif()
else()
set(USE_ALTERNATE_LINKER "" CACHE STRING "Use alternate linker" FORCE)
endif()
endmacro()
set(USE_ALTERNATE_LINKER "" CACHE STRING "Use alternate linker. Leave empty for system default; alternatives are 'gold', 'lld', 'bfd', 'mold'")
if(NOT "${USE_ALTERNATE_LINKER}" STREQUAL "")
set_alternate_linker(${USE_ALTERNATE_LINKER})
endif()
find_program(CCACHE_PROGRAM ccache)
if (CCACHE_PROGRAM)
set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
message("Using ${CCACHE_PROGRAM} for speeding up build")
endif ()
if (_compiler_launcher)
set(CMAKE_CXX_COMPILER_LAUNCHER "${_compiler_launcher}" CACHE STRING "CXX compiler launcher")
message(STATUS "Using ${_compiler_launcher} for speeding up build")
if (MSVC)
# /Zi can't be used with (s)ccache
# Use /Z7 instead (debug info in object files)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
endif()
endif()
endif()
include(${CMAKE_CURRENT_LIST_DIR}/cmake/GIT.cmake)
@ -48,20 +114,20 @@ find_package(Qt${MAJOR_QT_VERSION} REQUIRED
Widgets
Gui
Network
Multimedia
Svg
Concurrent
)
message(STATUS "Qt version: ${Qt${MAJOR_QT_VERSION}_VERSION}")
if (WIN32)
find_package(WinToast REQUIRED)
add_subdirectory(lib/WinToast EXCLUDE_FROM_ALL)
endif ()
find_package(Sanitizers)
find_package(Sanitizers QUIET)
# Find boost on the system
find_package(Boost REQUIRED)
find_package(Boost COMPONENTS random)
find_package(Boost REQUIRED OPTIONAL_COMPONENTS headers)
# Find OpenSSL on the system
find_package(OpenSSL REQUIRED)
@ -83,7 +149,6 @@ endif()
if (BUILD_WITH_QTKEYCHAIN)
# Link QtKeychain statically
option(QTKEYCHAIN_STATIC "" ON)
if (USE_SYSTEM_QTKEYCHAIN)
find_package(Qt${MAJOR_QT_VERSION}Keychain REQUIRED)
else()
@ -104,12 +169,17 @@ find_package(RapidJSON REQUIRED)
find_package(Websocketpp REQUIRED)
if (BUILD_TESTS)
include(GoogleTest)
# For MSVC: Prevent overriding the parent project's compiler/linker settings
# See https://github.com/google/googletest/blob/main/googletest/README.md#visual-studio-dynamic-vs-static-runtimes
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/lib/googletest" "lib/googletest")
mark_as_advanced(
BUILD_GMOCK BUILD_GTEST BUILD_SHARED_LIBS
gmock_build_tests gtest_build_samples gtest_build_tests
gtest_disable_pthreads gtest_force_shared_crt gtest_hide_internal_symbols
BUILD_GMOCK BUILD_GTEST BUILD_SHARED_LIBS
gmock_build_tests gtest_build_samples gtest_build_tests
gtest_disable_pthreads gtest_force_shared_crt gtest_hide_internal_symbols
)
set_target_properties(gtest PROPERTIES FOLDER lib)
@ -127,6 +197,7 @@ find_package(PajladaSerialize REQUIRED)
find_package(PajladaSignals REQUIRED)
find_package(LRUCache REQUIRED)
find_package(MagicEnum REQUIRED)
find_package(Doxygen)
if (USE_SYSTEM_PAJLADA_SETTINGS)
find_package(PajladaSettings REQUIRED)
@ -138,15 +209,37 @@ else()
add_subdirectory("${CMAKE_SOURCE_DIR}/lib/settings" EXCLUDE_FROM_ALL)
endif()
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (CHATTERINO_PLUGINS)
set(LUA_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/lib/lua/src")
add_subdirectory(lib/lua)
endif()
if (BUILD_TESTS OR BUILD_BENCHMARKS)
add_definitions(-DCHATTERINO_TEST)
if (BUILD_WITH_CRASHPAD)
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
# compilation in CMake is a more involved, as documented in https://stackoverflow.com/q/24292898.
# For CI runs, however, the date of build file generation should be consistent with the date of
# compilation so this approximation is "good enough" for our purpose.
if (DEFINED ENV{CHATTERINO_SKIP_DATE_GEN})
set(cmake_gen_date "1970-01-01")
else ()
string(TIMESTAMP cmake_gen_date "%Y-%m-%d")
endif ()
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Generate resource files
include(cmake/resources/generate_resources.cmake)
add_subdirectory(src)
if (BUILD_TESTS OR BUILD_BENCHMARKS)
add_subdirectory(mocks)
endif ()
if (BUILD_TESTS)
enable_testing()
add_subdirectory(tests)

View file

@ -2,6 +2,10 @@
This is a set of guidelines for contributing to Chatterino. The goal is to teach programmers without a C++ background (java/python/etc.), people who haven't used Qt, or otherwise have different experience, the idioms of the codebase. Thus we will focus on those which are different from those other environments. There are extra guidelines available [here](https://hackmd.io/@fourtf/chatterino-pendantic-guidelines) but they are considered as extras and not as important.
### General (non-code related) guidelines for contributing to Chatterino
- Make a specific branch for your pull request instead of using the master, main, or mainline branch. This will prevent future problems with updating your branch after your PR is merged.
# Tooling
## Formatting
@ -31,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!!!
*
@ -39,7 +43,7 @@ int compare(const QString &a, const QString &b);
* link
* ^^^ No need to repeat the obvious.
*/
boost::optional<QRegularExpressionMatch> matchLink(const QString &text);
std::optional<QRegularExpressionMatch> matchLink(const QString &text);
```
# Code

View file

@ -0,0 +1,5 @@
# https://www.qt.io/blog/qt-creator-cmake-package-manager-auto-setup
# set(QT_CREATOR_SKIP_PACKAGE_MANAGER_SETUP ON) # skip both conan and vcpkg auto-setups
# set(QT_CREATOR_SKIP_CONAN_SETUP ON) # skip conan auto-setup
set(QT_CREATOR_SKIP_VCPKG_SETUP ON) # skip vcpkg auto-setup

View file

@ -1,4 +1,4 @@
![alt text](https://fourtf.com/img/chatterino-icon-64.png)
![chatterinoLogo](https://user-images.githubusercontent.com/41973452/272541622-52457e89-5f16-4c83-93e7-91866c25b606.png)
Chatterino 2 [![GitHub Actions Build (Windows, Ubuntu, MacOS)](https://github.com/Chatterino/chatterino2/workflows/Build/badge.svg?branch=master)](https://github.com/Chatterino/chatterino2/actions?query=workflow%3ABuild+branch%3Amaster) [![Cirrus CI Build (FreeBSD only)](https://api.cirrus-ci.com/github/Chatterino/chatterino2.svg?branch=master)](https://cirrus-ci.com/github/Chatterino/chatterino2/master) [![Chocolatey Package](https://img.shields.io/chocolatey/v/chatterino?include_prereleases)](https://chocolatey.org/packages/chatterino) [![Flatpak Package](https://img.shields.io/flathub/v/com.chatterino.chatterino)](https://flathub.org/apps/details/com.chatterino.chatterino)
============
@ -22,43 +22,40 @@ 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](../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)
[Building on Windows with vcpkg](../master/BUILDING_ON_WINDOWS_WITH_VCPKG.md)
## Git blame
[Building on Linux](../master/BUILDING_ON_LINUX.md)
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:
[Building on Mac](../master/BUILDING_ON_MAC.md)
```shell
git config blame.ignoreRevsFile .git-blame-ignore-revs
```
[Building on FreeBSD](../master/BUILDING_ON_FREEBSD.md)
This will ignore all revisions mentioned in the [`.git-blame-ignore-revs`
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-11.0.0/LLVM-11.0.0-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,35 +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
IncludeBlocks: Preserve
NamespaceIndentation: Inner
PointerBindsToType: false
SpacesBeforeTrailingComments: 2
Standard: Auto
ReflowComments: false

View file

@ -1,12 +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
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!
)
@ -14,13 +18,10 @@ add_executable(${PROJECT_NAME} ${benchmark_SOURCES})
add_sanitizers(${PROJECT_NAME})
target_link_libraries(${PROJECT_NAME} PRIVATE chatterino-lib)
target_link_libraries(${PROJECT_NAME} PRIVATE chatterino-mocks)
target_link_libraries(${PROJECT_NAME} PRIVATE benchmark::benchmark)
target_compile_definitions(${PROJECT_NAME} PRIVATE
CHATTERINO_TEST
)
set_target_properties(${PROJECT_NAME}
PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
@ -29,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

@ -55,3 +55,128 @@ static void BM_ShortcodeParsing(benchmark::State &state)
}
BENCHMARK(BM_ShortcodeParsing);
static void BM_EmojiParsing(benchmark::State &state)
{
Emojis emojis;
emojis.load();
struct TestCase {
QString input;
std::vector<boost::variant<EmotePtr, QString>> expectedOutput;
};
const auto &emojiMap = emojis.getEmojis();
auto getEmoji = [&](auto code) {
std::shared_ptr<EmojiData> emoji;
for (const auto &e : emojis.getEmojis())
{
if (e->unifiedCode == code)
{
emoji = e;
break;
}
}
return emoji->emote;
};
auto penguinEmoji = getEmoji("1F427");
assert(penguinEmoji.get() != nullptr);
std::vector<TestCase> tests{
{
// 1 emoji
"foo 🐧 bar",
// expected output
{
"foo ",
penguinEmoji,
" bar",
},
},
{
// no emoji
"foo bar",
// expected output
{
"foo bar",
},
},
{
// many emoji
"foo 🐧 bar 🐧🐧🐧🐧🐧",
// expected output
{
"foo ",
penguinEmoji,
" bar ",
penguinEmoji,
penguinEmoji,
penguinEmoji,
penguinEmoji,
penguinEmoji,
},
},
};
for (auto _ : state)
{
for (const auto &test : tests)
{
auto output = emojis.parse(test.input);
bool areEqual = std::equal(output.begin(), output.end(),
test.expectedOutput.begin());
if (!areEqual)
{
qDebug() << "BAD BENCH";
for (const auto &v : output)
{
if (v.type() == typeid(QString))
{
qDebug() << "output:" << boost::get<QString>(v);
}
}
}
}
}
}
BENCHMARK(BM_EmojiParsing);
static void BM_EmojiParsing2(benchmark::State &state, const QString &input,
int expectedNumEmojis)
{
Emojis emojis;
emojis.load();
for (auto _ : state)
{
auto output = emojis.parse(input);
int actualNumEmojis = 0;
for (const auto &part : output)
{
if (part.type() == typeid(EmotePtr))
{
++actualNumEmojis;
}
}
if (actualNumEmojis != expectedNumEmojis)
{
qDebug() << "BAD BENCH, EXPECTED NUM EMOJIS IS WRONG"
<< actualNumEmojis;
}
}
}
BENCHMARK_CAPTURE(BM_EmojiParsing2, one_emoji, "foo 🐧 bar", 1);
BENCHMARK_CAPTURE(BM_EmojiParsing2, two_emoji, "foo 🐧 bar 🐧", 2);
BENCHMARK_CAPTURE(
BM_EmojiParsing2, many_emoji,
"😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 "
"😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 "
"😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 😂 ",
61);

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

@ -1,28 +1,31 @@
#include "Application.hpp"
#include "BaseSettings.hpp"
#include "common/Channel.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/highlights/HighlightController.hpp"
#include "controllers/highlights/HighlightPhrase.hpp"
#include "messages/Message.hpp"
#include "messages/SharedMessageBuilder.hpp"
#include "messages/MessageBuilder.hpp"
#include "mocks/BaseApplication.hpp"
#include "mocks/UserData.hpp"
#include "util/Helpers.hpp"
#include <benchmark/benchmark.h>
#include <QDebug>
#include <QString>
#include <QTemporaryDir>
using namespace chatterino;
class BenchmarkMessageBuilder : public SharedMessageBuilder
class BenchmarkMessageBuilder : public MessageBuilder
{
public:
explicit BenchmarkMessageBuilder(
Channel *_channel, const Communi::IrcPrivateMessage *_ircMessage,
const MessageParseArgs &_args)
: SharedMessageBuilder(_channel, _ircMessage, _args)
: MessageBuilder(_channel, _ircMessage, _args)
{
}
virtual MessagePtr build()
{
// PARSE
@ -45,71 +48,36 @@ public:
}
};
class MockApplication : IApplication
class MockApplication : public mock::BaseApplication
{
public:
Theme *getThemes() override
MockApplication()
: highlights(this->settings, &this->accounts)
{
return nullptr;
}
Fonts *getFonts() override
{
return nullptr;
}
Emotes *getEmotes() override
{
return nullptr;
}
AccountController *getAccounts() override
{
return &this->accounts;
}
HotkeyController *getHotkeys() override
{
return nullptr;
}
WindowManager *getWindows() override
{
return nullptr;
}
Toasts *getToasts() override
{
return nullptr;
}
CommandController *getCommands() override
{
return nullptr;
}
NotificationController *getNotifications() override
{
return nullptr;
}
HighlightController *getHighlights() override
{
return &this->highlights;
}
TwitchIrcServer *getTwitch() override
IUserDataController *getUserData() override
{
return nullptr;
}
ChatterinoBadges *getChatterinoBadges() override
{
return nullptr;
}
FfzBadges *getFfzBadges() override
{
return nullptr;
return &this->userData;
}
AccountController accounts;
HighlightController highlights;
// TODO: Figure this out
mock::UserDataController userData;
};
static void BM_HighlightTest(benchmark::State &state)
{
MockApplication mockApplication;
Settings settings("/tmp/c2-mock");
std::string message =
R"(@badge-info=subscriber/34;badges=moderator/1,subscriber/24;color=#FF0000;display-name=테스트계정420;emotes=41:6-13,15-22;flags=;id=a3196c7e-be4c-4b49-9c5a-8b8302b50c2a;mod=1;room-id=11148817;subscriber=1;tmi-sent-ts=1590922213730;turbo=0;user-id=117166826;user-type=mod :testaccount_420!testaccount_420@testaccount_420.tmi.twitch.tv PRIVMSG #pajlada :-tags Kreygasm,Kreygasm (no space))";

View file

@ -0,0 +1,39 @@
#include "common/LinkParser.hpp"
#include <benchmark/benchmark.h>
#include <QDebug>
#include <QString>
#include <QStringList>
using namespace chatterino;
const QString INPUT = QStringLiteral(
"If your Chatterino isn't loading FFZ emotes, update to the latest nightly "
"(or 2.4.2 if its out) "
"https://github.com/Chatterino/chatterino2/releases/tag/nightly-build "
"AlienPls https://www.youtube.com/watch?v=ELBBiBDcWc0 "
"127.0.3 aaaa xd 256.256.256.256 AsdQwe xd 127.0.0.1 https://. "
"*https://.be "
"https://a: http://a.b (https://a.be) ftp://xdd.com "
"this is a text lol . ://foo.com //aa.de :/foo.de xd.XDDDDDD ");
static void BM_LinkParsing(benchmark::State &state)
{
QStringList words = INPUT.split(' ');
// Make sure the TLDs are loaded
{
benchmark::DoNotOptimize(linkparser::parse("xd.com"));
}
for (auto _ : state)
{
for (const auto &word : words)
{
auto parsed = linkparser::parse(word);
benchmark::DoNotOptimize(parsed);
}
}
}
BENCHMARK(BM_LinkParsing);

View file

@ -0,0 +1,267 @@
#include "common/Literals.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/highlights/HighlightController.hpp"
#include "messages/Emote.hpp"
#include "mocks/BaseApplication.hpp"
#include "mocks/DisabledStreamerMode.hpp"
#include "mocks/LinkResolver.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 : public mock::BaseApplication
{
public:
MockApplication()
: highlights(this->settings, &this->accounts)
{
}
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;
}
ILinkResolver *getLinkResolver() override
{
return &this->linkResolver;
}
AccountController accounts;
Emotes emotes;
mock::UserDataController userData;
mock::MockTwitchIrcServer twitch;
mock::EmptyLinkResolver linkResolver;
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,18 +1,44 @@
#include "common/Args.hpp"
#include "singletons/Resources.hpp"
#include "singletons/Settings.hpp"
#include <benchmark/benchmark.h>
#include <QApplication>
#include <QtConcurrent>
#include <QTemporaryDir>
using namespace chatterino;
int main(int argc, char **argv)
{
QApplication app(argc, argv);
initResources();
::benchmark::Initialize(&argc, argv);
QtConcurrent::run([&app] {
Args args;
// Ensure settings are initialized before any benchmarks are run
QTemporaryDir settingsDir;
settingsDir.setAutoRemove(false); // we'll remove it manually
chatterino::Settings settings(args, settingsDir.path());
QTimer::singleShot(0, [&]() {
::benchmark::RunSpecifiedBenchmarks();
app.exit(0);
settingsDir.remove();
// Pick up the last events from the eventloop
// Using a loop to catch events queueing other events (e.g. deletions)
for (size_t i = 0; i < 32; i++)
{
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
}
QApplication::exit(0);
});
return app.exec();
return QApplication::exec();
}

719
cmake/CodeCoverage.cmake Normal file
View file

@ -0,0 +1,719 @@
# Copyright (c) 2012 - 2017, Lars Bilke
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# CHANGES:
#
# 2012-01-31, Lars Bilke
# - Enable Code Coverage
#
# 2013-09-17, Joakim Söderberg
# - Added support for Clang.
# - Some additional usage instructions.
#
# 2016-02-03, Lars Bilke
# - Refactored functions to use named parameters
#
# 2017-06-02, Lars Bilke
# - Merged with modified version from github.com/ufz/ogs
#
# 2019-05-06, Anatolii Kurotych
# - Remove unnecessary --coverage flag
#
# 2019-12-13, FeRD (Frank Dana)
# - Deprecate COVERAGE_LCOVR_EXCLUDES and COVERAGE_GCOVR_EXCLUDES lists in favor
# of tool-agnostic COVERAGE_EXCLUDES variable, or EXCLUDE setup arguments.
# - CMake 3.4+: All excludes can be specified relative to BASE_DIRECTORY
# - All setup functions: accept BASE_DIRECTORY, EXCLUDE list
# - Set lcov basedir with -b argument
# - Add automatic --demangle-cpp in lcovr, if 'c++filt' is available (can be
# overridden with NO_DEMANGLE option in setup_target_for_coverage_lcovr().)
# - Delete output dir, .info file on 'make clean'
# - Remove Python detection, since version mismatches will break gcovr
# - Minor cleanup (lowercase function names, update examples...)
#
# 2019-12-19, FeRD (Frank Dana)
# - Rename Lcov outputs, make filtered file canonical, fix cleanup for targets
#
# 2020-01-19, Bob Apthorpe
# - Added gfortran support
#
# 2020-02-17, FeRD (Frank Dana)
# - Make all add_custom_target()s VERBATIM to auto-escape wildcard characters
# in EXCLUDEs, and remove manual escaping from gcovr targets
#
# 2021-01-19, Robin Mueller
# - Add CODE_COVERAGE_VERBOSE option which will allow to print out commands which are run
# - Added the option for users to set the GCOVR_ADDITIONAL_ARGS variable to supply additional
# flags to the gcovr command
#
# 2020-05-04, Mihchael Davis
# - Add -fprofile-abs-path to make gcno files contain absolute paths
# - Fix BASE_DIRECTORY not working when defined
# - Change BYPRODUCT from folder to index.html to stop ninja from complaining about double defines
#
# 2021-05-10, Martin Stump
# - Check if the generator is multi-config before warning about non-Debug builds
#
# 2022-02-22, Marko Wehle
# - Change gcovr output from -o <filename> for --xml <filename> and --html <filename> output respectively.
# This will allow for Multiple Output Formats at the same time by making use of GCOVR_ADDITIONAL_ARGS, e.g. GCOVR_ADDITIONAL_ARGS "--txt".
#
# USAGE:
#
# 1. Copy this file into your cmake modules path.
#
# 2. Add the following line to your CMakeLists.txt (best inside an if-condition
# using a CMake option() to enable it just optionally):
# include(CodeCoverage)
#
# 3. Append necessary compiler flags for all supported source files:
# append_coverage_compiler_flags()
# Or for specific target:
# append_coverage_compiler_flags_to_target(YOUR_TARGET_NAME)
#
# 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og
#
# 4. If you need to exclude additional directories from the report, specify them
# using full paths in the COVERAGE_EXCLUDES variable before calling
# setup_target_for_coverage_*().
# Example:
# set(COVERAGE_EXCLUDES
# '${PROJECT_SOURCE_DIR}/src/dir1/*'
# '/path/to/my/src/dir2/*')
# Or, use the EXCLUDE argument to setup_target_for_coverage_*().
# Example:
# setup_target_for_coverage_lcov(
# NAME coverage
# EXECUTABLE testrunner
# EXCLUDE "${PROJECT_SOURCE_DIR}/src/dir1/*" "/path/to/my/src/dir2/*")
#
# 4.a NOTE: With CMake 3.4+, COVERAGE_EXCLUDES or EXCLUDE can also be set
# relative to the BASE_DIRECTORY (default: PROJECT_SOURCE_DIR)
# Example:
# set(COVERAGE_EXCLUDES "dir1/*")
# setup_target_for_coverage_gcovr_html(
# NAME coverage
# EXECUTABLE testrunner
# BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src"
# EXCLUDE "dir2/*")
#
# 5. Use the functions described below to create a custom make target which
# runs your test executable and produces a code coverage report.
#
# 6. Build a Debug build:
# cmake -DCMAKE_BUILD_TYPE=Debug ..
# make
# make my_coverage_target
#
include(CMakeParseArguments)
option(CODE_COVERAGE_VERBOSE "Verbose information" FALSE)
# Check prereqs
find_program( GCOV_PATH gcov )
find_program( LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl)
find_program( FASTCOV_PATH NAMES fastcov fastcov.py )
find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat )
find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test)
find_program( CPPFILT_PATH NAMES c++filt )
if(NOT GCOV_PATH)
message(FATAL_ERROR "gcov not found! Aborting...")
endif() # NOT GCOV_PATH
get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
list(GET LANGUAGES 0 LANG)
if("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
if("${CMAKE_${LANG}_COMPILER_VERSION}" VERSION_LESS 3)
message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
endif()
elseif(NOT CMAKE_COMPILER_IS_GNUCXX)
if("${CMAKE_Fortran_COMPILER_ID}" MATCHES "[Ff]lang")
# Do nothing; exit conditional without error if true
elseif("${CMAKE_Fortran_COMPILER_ID}" MATCHES "GNU")
# Do nothing; exit conditional without error if true
else()
message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
endif()
endif()
set(COVERAGE_COMPILER_FLAGS "-g -fprofile-arcs -ftest-coverage"
CACHE INTERNAL "")
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag(-fprofile-abs-path HAVE_fprofile_abs_path)
if(HAVE_fprofile_abs_path)
set(COVERAGE_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path")
endif()
endif()
set(CMAKE_Fortran_FLAGS_COVERAGE
${COVERAGE_COMPILER_FLAGS}
CACHE STRING "Flags used by the Fortran compiler during coverage builds."
FORCE )
set(CMAKE_CXX_FLAGS_COVERAGE
${COVERAGE_COMPILER_FLAGS}
CACHE STRING "Flags used by the C++ compiler during coverage builds."
FORCE )
set(CMAKE_C_FLAGS_COVERAGE
${COVERAGE_COMPILER_FLAGS}
CACHE STRING "Flags used by the C compiler during coverage builds."
FORCE )
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used for linking binaries during coverage builds."
FORCE )
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used by the shared libraries linker during coverage builds."
FORCE )
mark_as_advanced(
CMAKE_Fortran_FLAGS_COVERAGE
CMAKE_CXX_FLAGS_COVERAGE
CMAKE_C_FLAGS_COVERAGE
CMAKE_EXE_LINKER_FLAGS_COVERAGE
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG))
message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
endif() # NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG)
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
link_libraries(gcov)
endif()
# Defines a target for running and collection code coverage information
# Builds dependencies, runs the given executable and outputs reports.
# NOTE! The executable should always have a ZERO as exit code otherwise
# the coverage generation will not complete.
#
# setup_target_for_coverage_lcov(
# NAME testrunner_coverage # New target name
# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
# DEPENDENCIES testrunner # Dependencies to build first
# BASE_DIRECTORY "../" # Base directory for report
# # (defaults to PROJECT_SOURCE_DIR)
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
# # to BASE_DIRECTORY, with CMake 3.4+)
# NO_DEMANGLE # Don't demangle C++ symbols
# # even if c++filt is found
# )
function(setup_target_for_coverage_lcov)
set(options NO_DEMANGLE)
set(oneValueArgs BASE_DIRECTORY NAME)
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS)
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT LCOV_PATH)
message(FATAL_ERROR "lcov not found! Aborting...")
endif() # NOT LCOV_PATH
if(NOT GENHTML_PATH)
message(FATAL_ERROR "genhtml not found! Aborting...")
endif() # NOT GENHTML_PATH
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
if(DEFINED Coverage_BASE_DIRECTORY)
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
else()
set(BASEDIR ${PROJECT_SOURCE_DIR})
endif()
# Collect excludes (CMake 3.4+: Also compute absolute paths)
set(LCOV_EXCLUDES "")
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_LCOV_EXCLUDES})
if(CMAKE_VERSION VERSION_GREATER 3.4)
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
endif()
list(APPEND LCOV_EXCLUDES "${EXCLUDE}")
endforeach()
list(REMOVE_DUPLICATES LCOV_EXCLUDES)
# Conditional arguments
if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE})
set(GENHTML_EXTRA_ARGS "--demangle-cpp")
endif()
# Setting up commands which will be run to generate coverage data.
# Cleanup lcov
set(LCOV_CLEAN_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory .
-b ${BASEDIR} --zerocounters
)
# Create baseline to make sure untouched files show up in the report
set(LCOV_BASELINE_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c -i -d . -b
${BASEDIR} -o ${Coverage_NAME}.base
)
# Run tests
set(LCOV_EXEC_TESTS_CMD
${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
)
# Capturing lcov counters and generating report
set(LCOV_CAPTURE_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . -b
${BASEDIR} --capture --output-file ${Coverage_NAME}.capture
)
# add baseline counters
set(LCOV_BASELINE_COUNT_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base
-a ${Coverage_NAME}.capture --output-file ${Coverage_NAME}.total
)
# filter collected data to final coverage report
set(LCOV_FILTER_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove
${Coverage_NAME}.total ${LCOV_EXCLUDES} --output-file ${Coverage_NAME}.info
)
# Generate HTML output
set(LCOV_GEN_HTML_CMD
${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o
${Coverage_NAME} ${Coverage_NAME}.info
)
if(CODE_COVERAGE_VERBOSE)
message(STATUS "Executed command report")
message(STATUS "Command to clean up lcov: ")
string(REPLACE ";" " " LCOV_CLEAN_CMD_SPACED "${LCOV_CLEAN_CMD}")
message(STATUS "${LCOV_CLEAN_CMD_SPACED}")
message(STATUS "Command to create baseline: ")
string(REPLACE ";" " " LCOV_BASELINE_CMD_SPACED "${LCOV_BASELINE_CMD}")
message(STATUS "${LCOV_BASELINE_CMD_SPACED}")
message(STATUS "Command to run the tests: ")
string(REPLACE ";" " " LCOV_EXEC_TESTS_CMD_SPACED "${LCOV_EXEC_TESTS_CMD}")
message(STATUS "${LCOV_EXEC_TESTS_CMD_SPACED}")
message(STATUS "Command to capture counters and generate report: ")
string(REPLACE ";" " " LCOV_CAPTURE_CMD_SPACED "${LCOV_CAPTURE_CMD}")
message(STATUS "${LCOV_CAPTURE_CMD_SPACED}")
message(STATUS "Command to add baseline counters: ")
string(REPLACE ";" " " LCOV_BASELINE_COUNT_CMD_SPACED "${LCOV_BASELINE_COUNT_CMD}")
message(STATUS "${LCOV_BASELINE_COUNT_CMD_SPACED}")
message(STATUS "Command to filter collected data: ")
string(REPLACE ";" " " LCOV_FILTER_CMD_SPACED "${LCOV_FILTER_CMD}")
message(STATUS "${LCOV_FILTER_CMD_SPACED}")
message(STATUS "Command to generate lcov HTML output: ")
string(REPLACE ";" " " LCOV_GEN_HTML_CMD_SPACED "${LCOV_GEN_HTML_CMD}")
message(STATUS "${LCOV_GEN_HTML_CMD_SPACED}")
endif()
# Setup target
add_custom_target(${Coverage_NAME}
COMMAND ${LCOV_CLEAN_CMD}
COMMAND ${LCOV_BASELINE_CMD}
COMMAND ${LCOV_EXEC_TESTS_CMD}
COMMAND ${LCOV_CAPTURE_CMD}
COMMAND ${LCOV_BASELINE_COUNT_CMD}
COMMAND ${LCOV_FILTER_CMD}
COMMAND ${LCOV_GEN_HTML_CMD}
# Set output files as GENERATED (will be removed on 'make clean')
BYPRODUCTS
${Coverage_NAME}.base
${Coverage_NAME}.capture
${Coverage_NAME}.total
${Coverage_NAME}.info
${Coverage_NAME}/index.html
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${Coverage_DEPENDENCIES}
VERBATIM # Protect arguments to commands
COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
)
# Show where to find the lcov info report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ;
COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info."
)
# Show info where to find the report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ;
COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
)
endfunction() # setup_target_for_coverage_lcov
# Defines a target for running and collection code coverage information
# Builds dependencies, runs the given executable and outputs reports.
# NOTE! The executable should always have a ZERO as exit code otherwise
# the coverage generation will not complete.
#
# setup_target_for_coverage_gcovr_xml(
# NAME ctest_coverage # New target name
# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
# DEPENDENCIES executable_target # Dependencies to build first
# BASE_DIRECTORY "../" # Base directory for report
# # (defaults to PROJECT_SOURCE_DIR)
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
# # to BASE_DIRECTORY, with CMake 3.4+)
# )
# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the
# GCVOR command.
function(setup_target_for_coverage_gcovr_xml)
set(options NONE)
set(oneValueArgs BASE_DIRECTORY NAME)
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT GCOVR_PATH)
message(FATAL_ERROR "gcovr not found! Aborting...")
endif() # NOT GCOVR_PATH
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
if(DEFINED Coverage_BASE_DIRECTORY)
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
else()
set(BASEDIR ${PROJECT_SOURCE_DIR})
endif()
# Collect excludes (CMake 3.4+: Also compute absolute paths)
set(GCOVR_EXCLUDES "")
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES})
if(CMAKE_VERSION VERSION_GREATER 3.4)
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
endif()
list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
endforeach()
list(REMOVE_DUPLICATES GCOVR_EXCLUDES)
# Combine excludes to several -e arguments
set(GCOVR_EXCLUDE_ARGS "")
foreach(EXCLUDE ${GCOVR_EXCLUDES})
list(APPEND GCOVR_EXCLUDE_ARGS "-e")
list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}")
endforeach()
# Set up commands which will be run to generate coverage data
# Run tests
set(GCOVR_XML_EXEC_TESTS_CMD
${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
)
# Running gcovr
set(GCOVR_XML_CMD
${GCOVR_PATH} --xml ${Coverage_NAME}.xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS}
${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR}
)
if(CODE_COVERAGE_VERBOSE)
message(STATUS "Executed command report")
message(STATUS "Command to run tests: ")
string(REPLACE ";" " " GCOVR_XML_EXEC_TESTS_CMD_SPACED "${GCOVR_XML_EXEC_TESTS_CMD}")
message(STATUS "${GCOVR_XML_EXEC_TESTS_CMD_SPACED}")
message(STATUS "Command to generate gcovr XML coverage data: ")
string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}")
message(STATUS "${GCOVR_XML_CMD_SPACED}")
endif()
add_custom_target(${Coverage_NAME}
COMMAND ${GCOVR_XML_EXEC_TESTS_CMD}
COMMAND ${GCOVR_XML_CMD}
BYPRODUCTS ${Coverage_NAME}.xml
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${Coverage_DEPENDENCIES}
VERBATIM # Protect arguments to commands
COMMENT "Running gcovr to produce Cobertura code coverage report."
)
# Show info where to find the report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ;
COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml."
)
endfunction() # setup_target_for_coverage_gcovr_xml
# Defines a target for running and collection code coverage information
# Builds dependencies, runs the given executable and outputs reports.
# NOTE! The executable should always have a ZERO as exit code otherwise
# the coverage generation will not complete.
#
# setup_target_for_coverage_gcovr_html(
# NAME ctest_coverage # New target name
# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
# DEPENDENCIES executable_target # Dependencies to build first
# BASE_DIRECTORY "../" # Base directory for report
# # (defaults to PROJECT_SOURCE_DIR)
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
# # to BASE_DIRECTORY, with CMake 3.4+)
# )
# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the
# GCVOR command.
function(setup_target_for_coverage_gcovr_html)
set(options NONE)
set(oneValueArgs BASE_DIRECTORY NAME)
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT GCOVR_PATH)
message(FATAL_ERROR "gcovr not found! Aborting...")
endif() # NOT GCOVR_PATH
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
if(DEFINED Coverage_BASE_DIRECTORY)
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
else()
set(BASEDIR ${PROJECT_SOURCE_DIR})
endif()
# Collect excludes (CMake 3.4+: Also compute absolute paths)
set(GCOVR_EXCLUDES "")
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES})
if(CMAKE_VERSION VERSION_GREATER 3.4)
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
endif()
list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
endforeach()
list(REMOVE_DUPLICATES GCOVR_EXCLUDES)
# Combine excludes to several -e arguments
set(GCOVR_EXCLUDE_ARGS "")
foreach(EXCLUDE ${GCOVR_EXCLUDES})
list(APPEND GCOVR_EXCLUDE_ARGS "-e")
list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}")
endforeach()
# Set up commands which will be run to generate coverage data
# Run tests
set(GCOVR_HTML_EXEC_TESTS_CMD
${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
)
# Create folder
set(GCOVR_HTML_FOLDER_CMD
${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME}
)
# Running gcovr
set(GCOVR_HTML_CMD
${GCOVR_PATH} --html ${Coverage_NAME}/index.html --html-details -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS}
${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR}
)
if(CODE_COVERAGE_VERBOSE)
message(STATUS "Executed command report")
message(STATUS "Command to run tests: ")
string(REPLACE ";" " " GCOVR_HTML_EXEC_TESTS_CMD_SPACED "${GCOVR_HTML_EXEC_TESTS_CMD}")
message(STATUS "${GCOVR_HTML_EXEC_TESTS_CMD_SPACED}")
message(STATUS "Command to create a folder: ")
string(REPLACE ";" " " GCOVR_HTML_FOLDER_CMD_SPACED "${GCOVR_HTML_FOLDER_CMD}")
message(STATUS "${GCOVR_HTML_FOLDER_CMD_SPACED}")
message(STATUS "Command to generate gcovr HTML coverage data: ")
string(REPLACE ";" " " GCOVR_HTML_CMD_SPACED "${GCOVR_HTML_CMD}")
message(STATUS "${GCOVR_HTML_CMD_SPACED}")
endif()
add_custom_target(${Coverage_NAME}
COMMAND ${GCOVR_HTML_EXEC_TESTS_CMD}
COMMAND ${GCOVR_HTML_FOLDER_CMD}
COMMAND ${GCOVR_HTML_CMD}
BYPRODUCTS ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html # report directory
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${Coverage_DEPENDENCIES}
VERBATIM # Protect arguments to commands
COMMENT "Running gcovr to produce HTML code coverage report."
)
# Show info where to find the report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ;
COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
)
endfunction() # setup_target_for_coverage_gcovr_html
# Defines a target for running and collection code coverage information
# Builds dependencies, runs the given executable and outputs reports.
# NOTE! The executable should always have a ZERO as exit code otherwise
# the coverage generation will not complete.
#
# setup_target_for_coverage_fastcov(
# NAME testrunner_coverage # New target name
# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
# DEPENDENCIES testrunner # Dependencies to build first
# BASE_DIRECTORY "../" # Base directory for report
# # (defaults to PROJECT_SOURCE_DIR)
# EXCLUDE "src/dir1/" "src/dir2/" # Patterns to exclude.
# NO_DEMANGLE # Don't demangle C++ symbols
# # even if c++filt is found
# SKIP_HTML # Don't create html report
# POST_CMD perl -i -pe s!${PROJECT_SOURCE_DIR}/!!g ctest_coverage.json # E.g. for stripping source dir from file paths
# )
function(setup_target_for_coverage_fastcov)
set(options NO_DEMANGLE SKIP_HTML)
set(oneValueArgs BASE_DIRECTORY NAME)
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES FASTCOV_ARGS GENHTML_ARGS POST_CMD)
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT FASTCOV_PATH)
message(FATAL_ERROR "fastcov not found! Aborting...")
endif()
if(NOT Coverage_SKIP_HTML AND NOT GENHTML_PATH)
message(FATAL_ERROR "genhtml not found! Aborting...")
endif()
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
if(Coverage_BASE_DIRECTORY)
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
else()
set(BASEDIR ${PROJECT_SOURCE_DIR})
endif()
# Collect excludes (Patterns, not paths, for fastcov)
set(FASTCOV_EXCLUDES "")
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_FASTCOV_EXCLUDES})
list(APPEND FASTCOV_EXCLUDES "${EXCLUDE}")
endforeach()
list(REMOVE_DUPLICATES FASTCOV_EXCLUDES)
# Conditional arguments
if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE})
set(GENHTML_EXTRA_ARGS "--demangle-cpp")
endif()
# Set up commands which will be run to generate coverage data
set(FASTCOV_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS})
set(FASTCOV_CAPTURE_CMD ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH}
--search-directory ${BASEDIR}
--process-gcno
--output ${Coverage_NAME}.json
--exclude ${FASTCOV_EXCLUDES}
--exclude ${FASTCOV_EXCLUDES}
)
set(FASTCOV_CONVERT_CMD ${FASTCOV_PATH}
-C ${Coverage_NAME}.json --lcov --output ${Coverage_NAME}.info
)
if(Coverage_SKIP_HTML)
set(FASTCOV_HTML_CMD ";")
else()
set(FASTCOV_HTML_CMD ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS}
-o ${Coverage_NAME} ${Coverage_NAME}.info
)
endif()
set(FASTCOV_POST_CMD ";")
if(Coverage_POST_CMD)
set(FASTCOV_POST_CMD ${Coverage_POST_CMD})
endif()
if(CODE_COVERAGE_VERBOSE)
message(STATUS "Code coverage commands for target ${Coverage_NAME} (fastcov):")
message(" Running tests:")
string(REPLACE ";" " " FASTCOV_EXEC_TESTS_CMD_SPACED "${FASTCOV_EXEC_TESTS_CMD}")
message(" ${FASTCOV_EXEC_TESTS_CMD_SPACED}")
message(" Capturing fastcov counters and generating report:")
string(REPLACE ";" " " FASTCOV_CAPTURE_CMD_SPACED "${FASTCOV_CAPTURE_CMD}")
message(" ${FASTCOV_CAPTURE_CMD_SPACED}")
message(" Converting fastcov .json to lcov .info:")
string(REPLACE ";" " " FASTCOV_CONVERT_CMD_SPACED "${FASTCOV_CONVERT_CMD}")
message(" ${FASTCOV_CONVERT_CMD_SPACED}")
if(NOT Coverage_SKIP_HTML)
message(" Generating HTML report: ")
string(REPLACE ";" " " FASTCOV_HTML_CMD_SPACED "${FASTCOV_HTML_CMD}")
message(" ${FASTCOV_HTML_CMD_SPACED}")
endif()
if(Coverage_POST_CMD)
message(" Running post command: ")
string(REPLACE ";" " " FASTCOV_POST_CMD_SPACED "${FASTCOV_POST_CMD}")
message(" ${FASTCOV_POST_CMD_SPACED}")
endif()
endif()
# Setup target
add_custom_target(${Coverage_NAME}
# Cleanup fastcov
COMMAND ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH}
--search-directory ${BASEDIR}
--zerocounters
COMMAND ${FASTCOV_EXEC_TESTS_CMD}
COMMAND ${FASTCOV_CAPTURE_CMD}
COMMAND ${FASTCOV_CONVERT_CMD}
COMMAND ${FASTCOV_HTML_CMD}
COMMAND ${FASTCOV_POST_CMD}
# Set output files as GENERATED (will be removed on 'make clean')
BYPRODUCTS
${Coverage_NAME}.info
${Coverage_NAME}.json
${Coverage_NAME}/index.html # report directory
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${Coverage_DEPENDENCIES}
VERBATIM # Protect arguments to commands
COMMENT "Resetting code coverage counters to zero. Processing code coverage counters and generating report."
)
set(INFO_MSG "fastcov code coverage info report saved in ${Coverage_NAME}.info and ${Coverage_NAME}.json.")
if(NOT Coverage_SKIP_HTML)
string(APPEND INFO_MSG " Open ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html in your browser to view the coverage report.")
endif()
# Show where to find the fastcov info report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo ${INFO_MSG}
)
endfunction() # setup_target_for_coverage_fastcov
function(append_coverage_compiler_flags)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}")
endfunction() # append_coverage_compiler_flags
# Setup coverage for specific library
function(append_coverage_compiler_flags_to_target name)
separate_arguments(_flag_list NATIVE_COMMAND "${COVERAGE_COMPILER_FLAGS}")
target_compile_options(${name} PRIVATE ${_flag_list})
endfunction()

View file

@ -1,6 +1,6 @@
include(FindPackageHandleStandardArgs)
find_path(MagicEnum_INCLUDE_DIR magic_enum.hpp HINTS ${CMAKE_SOURCE_DIR}/lib/magic_enum/include)
find_path(MagicEnum_INCLUDE_DIR magic_enum/magic_enum.hpp HINTS ${CMAKE_SOURCE_DIR}/lib/magic_enum/include)
find_package_handle_standard_args(MagicEnum DEFAULT_MSG MagicEnum_INCLUDE_DIR)

View file

@ -1,12 +0,0 @@
if (EXISTS ${CMAKE_SOURCE_DIR}/lib/WinToast/src/wintoastlib.cpp)
set(WinToast_FOUND TRUE)
add_library(WinToast ${CMAKE_SOURCE_DIR}/lib/WinToast/src/wintoastlib.cpp)
target_include_directories(WinToast PUBLIC "${CMAKE_SOURCE_DIR}/lib/WinToast/src/")
else ()
set(WinToast_FOUND FALSE)
message("WinToast submodule not found!")
endif ()

View file

@ -9,7 +9,7 @@
# If the git binary is found and the git work tree is intact, GIT_RELEASE is worked out using the `git describe` command
# The value of GIT_RELEASE can be overriden by defining the GIT_RELEASE environment variable
# GIT_MODIFIED
# If the git binary is found and the git work tree is intact, GIT_MODIFIED is worked out by checking if output of `git status --porcelain -z` command is empty
# If the git binary is found and the git work tree is intact, GIT_MODIFIED is worked out by checking if output of `git status --porcelain -z` command is empty
# The value of GIT_MODIFIED cannot be overriden
find_package(Git)
@ -19,66 +19,75 @@ set(GIT_COMMIT "GIT-REPOSITORY-NOT-FOUND")
set(GIT_RELEASE "${PROJECT_VERSION}")
set(GIT_MODIFIED 0)
if (DEFINED ENV{CHATTERINO_SKIP_GIT_GEN})
if(DEFINED ENV{CHATTERINO_SKIP_GIT_GEN})
return()
endif ()
endif()
if (GIT_EXECUTABLE)
if(GIT_EXECUTABLE)
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse --is-inside-work-tree
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
RESULT_VARIABLE GIT_REPOSITORY_NOT_FOUND
ERROR_QUIET
COMMAND ${GIT_EXECUTABLE} rev-parse --is-inside-work-tree
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
RESULT_VARIABLE GIT_REPOSITORY_NOT_FOUND
OUTPUT_QUIET
ERROR_QUIET
)
if (GIT_REPOSITORY_NOT_FOUND)
if(GIT_REPOSITORY_NOT_FOUND)
set(GIT_REPOSITORY_FOUND 0)
else ()
else()
set(GIT_REPOSITORY_FOUND 1)
endif()
if (GIT_REPOSITORY_FOUND)
if(GIT_REPOSITORY_FOUND)
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT
OUTPUT_STRIP_TRAILING_WHITESPACE
COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process(
COMMAND ${GIT_EXECUTABLE} describe
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_RELEASE
OUTPUT_STRIP_TRAILING_WHITESPACE
COMMAND ${GIT_EXECUTABLE} describe
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_RELEASE
OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process(
COMMAND ${GIT_EXECUTABLE} status --porcelain -z
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_MODIFIED_OUTPUT
OUTPUT_STRIP_TRAILING_WHITESPACE
COMMAND ${GIT_EXECUTABLE} status --porcelain -z
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_MODIFIED_OUTPUT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
endif (GIT_REPOSITORY_FOUND)
endif (GIT_EXECUTABLE)
endif(GIT_REPOSITORY_FOUND)
endif(GIT_EXECUTABLE)
if(GIT_MODIFIED_OUTPUT)
if(DEFINED ENV{CHATTERINO_REQUIRE_CLEAN_GIT})
message(STATUS "git status --porcelain -z\n${GIT_MODIFIED_OUTPUT}")
message(FATAL_ERROR "Git repository was expected to be clean, but modifications were found!")
endif()
if (GIT_MODIFIED_OUTPUT)
set(GIT_MODIFIED 1)
endif ()
endif()
if (DEFINED ENV{GIT_HASH})
if(DEFINED ENV{GIT_HASH})
set(GIT_HASH "$ENV{GIT_HASH}")
endif ()
if (DEFINED ENV{GIT_COMMIT})
endif()
if(DEFINED ENV{GIT_COMMIT})
set(GIT_COMMIT "$ENV{GIT_COMMIT}")
endif ()
if (DEFINED ENV{GIT_RELEASE})
endif()
if(DEFINED ENV{GIT_RELEASE})
set(GIT_RELEASE "$ENV{GIT_RELEASE}")
endif ()
endif()
message(STATUS "Injected git values: ${GIT_COMMIT} (${GIT_RELEASE}) modified: ${GIT_MODIFIED}")

View file

@ -6,8 +6,6 @@
<string>English</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleGetInfoString</key>
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
<key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
<key>CFBundleIdentifier</key>

View file

@ -0,0 +1,13 @@
/****************************************************************************
** WARNING! This file is autogenerated by cmake
** WARNING! All changes made in this file will be lost!
*****************************************************************************/
#include "ResourcesAutogen.hpp"
namespace chatterino {
Resources2::Resources2()
{
@RES_SOURCE_CONTENT@
}
} // namespace chatterino

View file

@ -0,0 +1,16 @@
/****************************************************************************
** WARNING! This file is autogenerated by cmake
** WARNING! All changes made in this file will be lost!
*****************************************************************************/
#include <QPixmap>
namespace chatterino {
class Resources2
{
public:
Resources2();
@RES_HEADER_CONTENT@
};
} // namespace chatterino

View file

@ -0,0 +1,95 @@
set(RES_DIR "${CMAKE_SOURCE_DIR}/resources")
# Note: files in this ignorelist should be relative to ${RES_DIR}
set(
RES_IGNORED_FILES
.gitignore
qt.conf
resources.qrc
resources_autogenerated.qrc
themes/ChatterinoTheme.schema.json
)
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})
###############################
# Generate resources_autogenerated.qrc
###############################
message(STATUS "Generating resources_autogenerated.qrc")
foreach (_file ${RES_ALL_FILES})
list(APPEND RES_RESOURCES_CONTENT " <file>${_file}</file>")
endforeach ()
list(JOIN RES_RESOURCES_CONTENT "\n" RES_RESOURCES_CONTENT)
configure_file(${CMAKE_CURRENT_LIST_DIR}/resources_autogenerated.qrc.in ${RES_DIR}/resources_autogenerated.qrc @ONLY)
###############################
# Generate ResourcesAutogen.cpp
###############################
message(STATUS "Generating ResourcesAutogen.cpp")
foreach (_file ${RES_IMAGE_FILES})
get_filename_component(_ext "${_file}" EXT)
string(REPLACE "${_ext}" "" _var_name ${_file})
string(REPLACE "/" "." _var_name ${_var_name})
list(APPEND RES_SOURCE_CONTENT " this->${_var_name} = QPixmap(\":/${_file}\")\;")
list(APPEND RES_VAR_NAMES "${_var_name}")
endforeach ()
list(JOIN RES_SOURCE_CONTENT "\n" RES_SOURCE_CONTENT)
configure_file(${CMAKE_CURRENT_LIST_DIR}/ResourcesAutogen.cpp.in ${CMAKE_BINARY_DIR}/autogen/ResourcesAutogen.cpp @ONLY)
###############################
# Generate ResourcesAutogen.hpp
###############################
message(STATUS "Generating ResourcesAutogen.hpp")
set(_struct_list "")
foreach (_file ${RES_IMAGE_FILES})
get_filename_component(_dir "${_file}" DIRECTORY)
get_filename_component(_name "${_file}" NAME_WE)
string(REPLACE "/" "_" _dir "${_dir}")
if (NOT _dir)
set(_dir "root")
endif ()
list(APPEND ${_dir} "${_name}")
list(APPEND _struct_list "${_dir}")
endforeach ()
list(REMOVE_DUPLICATES _struct_list)
foreach (_str_name ${_struct_list})
if (NOT "${_str_name}" STREQUAL "root")
list(APPEND RES_HEADER_CONTENT " struct {")
set(_indent " ")
else ()
set(_indent " ")
endif ()
foreach (_name ${${_str_name}})
list(APPEND RES_HEADER_CONTENT "${_indent}QPixmap ${_name}\;")
endforeach ()
if (NOT "${_str_name}" STREQUAL "root")
list(APPEND RES_HEADER_CONTENT " } ${_str_name}\;")
endif ()
endforeach ()
list(JOIN RES_HEADER_CONTENT "\n" RES_HEADER_CONTENT)
configure_file(${CMAKE_CURRENT_LIST_DIR}/ResourcesAutogen.hpp.in ${CMAKE_BINARY_DIR}/autogen/ResourcesAutogen.hpp @ONLY)
if (WIN32)
if (NOT PROJECT_VERSION_TWEAK)
set(PROJECT_VERSION_TWEAK 0)
endif()
string(TIMESTAMP CURRENT_YEAR "%Y")
configure_file(${CMAKE_CURRENT_LIST_DIR}/windows.rc.in ${CMAKE_BINARY_DIR}/autogen/windows.rc @ONLY)
list(APPEND RES_AUTOGEN_FILES "${CMAKE_BINARY_DIR}/autogen/windows.rc")
endif ()
list(APPEND RES_AUTOGEN_FILES
"${CMAKE_SOURCE_DIR}/resources/resources_autogenerated.qrc"
"${CMAKE_BINARY_DIR}/autogen/ResourcesAutogen.cpp"
"${CMAKE_BINARY_DIR}/autogen/ResourcesAutogen.hpp"
)

View file

@ -0,0 +1,9 @@
<!--
WARNING! This file is autogenerated by cmake
WARNING! All changes made in this file will be lost!
-->
<RCC>
<qresource prefix="/">
@RES_RESOURCES_CONTENT@
</qresource>
</RCC>

View file

@ -0,0 +1,34 @@
#include <winver.h>
IDI_ICON1 ICON "@RES_DIR@/icon.ico"
VS_VERSION_INFO VERSIONINFO
FILEVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,@PROJECT_VERSION_TWEAK@
PRODUCTVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,@PROJECT_VERSION_TWEAK@
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
FILEFLAGS VS_FF_SPECIALBUILD
FILEOS VOS__WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "ProductName", "Chatterino"
VALUE "ProductVersion", "@PROJECT_VERSION@"
VALUE "CompanyName", "Chatterino, @PROJECT_HOMEPAGE_URL@"
VALUE "FileDescription", "Chatterino"
VALUE "FileVersion", "@PROJECT_VERSION@"
VALUE "SpecialBuild", "@GIT_COMMIT@"
VALUE "InternalName", "Chatterino"
VALUE "OriginalFilename", "Chatterino"
VALUE "LegalCopyright", "Project contributors 2016-@CURRENT_YEAR@"
VALUE "Licence", "MIT"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

@ -1 +1 @@
Subproject commit 99e159ec9bc8dd362b08d18436bd40ff0648417b
Subproject commit 3f0542e4e034aab417c51b2b22c94f83355dee15

55
conanfile.py Normal file
View file

@ -0,0 +1,55 @@
from conan import ConanFile
from conan.tools.files import copy
from os import path
class Chatterino(ConanFile):
name = "Chatterino"
requires = "boost/1.83.0"
settings = "os", "compiler", "build_type", "arch"
default_options = {
"with_benchmark": False,
"with_openssl3": True,
"openssl*:shared": True,
"boost*:header_only": True,
}
options = {
"with_benchmark": [True, False],
# Qt is built with OpenSSL 3 from version 6.5.0 onwards
"with_openssl3": [True, False],
}
generators = "CMakeDeps", "CMakeToolchain"
def requirements(self):
if self.options.get_safe("with_benchmark", False):
self.requires("benchmark/1.7.1")
if self.options.get_safe("with_openssl3", False):
self.requires("openssl/3.2.0")
else:
self.requires("openssl/1.1.1t")
def generate(self):
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")
# Windows
copy_bin(dep, "*.dll", "bin")
copy_bin(dep, "*.dll", "Chatterino2") # used in CI
# Linux
copy(
self,
"*.so*",
dep.cpp_info.libdirs[0],
path.join(self.build_folder, "bin"),
keep_path=False,
)

View file

@ -1,14 +0,0 @@
[requires]
openssl/1.1.1m
boost/1.78.0
[generators]
cmake
[options]
openssl:shared=True
[imports]
bin, *.dll -> ./bin @ keep_path=False
bin, *.dll -> ./Chatterino2 @ keep_path=False
lib, *.so* -> ./bin @ keep_path=False

View file

@ -0,0 +1,405 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"title": "Chatterino Theme",
"description": "Colors and metadata for a Chatterino 2 theme",
"definitions": {
"qt-color": {
"type": "string",
"$comment": "https://doc.qt.io/qt-5/qcolor.html#setNamedColor",
"anyOf": [
{
"title": "#RGB",
"pattern": "^#[a-fA-F0-9]{3}$"
},
{
"title": "#RRGGBB",
"pattern": "^#[a-fA-F0-9]{6}$"
},
{
"title": "#AARRGGBB",
"$comment": "Note that this isn't identical to the CSS Color Moudle Level 4 <hex-color> where the alpha value is at the end.",
"pattern": "^#[a-fA-F0-9]{8}$"
},
{
"title": "#RRRGGGBBB",
"pattern": "^#[a-fA-F0-9]{9}$"
},
{
"title": "#RRRRGGGGBBBB",
"pattern": "^#[a-fA-F0-9]{12}$"
},
{
"title": "SVG Color",
"description": "This enum is stricter than Qt. You could theoretically put tabs and spaces between characters in a named color and capitalize the color.",
"$comment": "https://www.w3.org/TR/SVG11/types.html#ColorKeywords",
"enum": [
"aliceblue",
"antiquewhite",
"aqua",
"aquamarine",
"azure",
"beige",
"bisque",
"black",
"blanchedalmond",
"blue",
"blueviolet",
"brown",
"burlywood",
"cadetblue",
"chartreuse",
"chocolate",
"coral",
"cornflowerblue",
"cornsilk",
"crimson",
"cyan",
"darkblue",
"darkcyan",
"darkgoldenrod",
"darkgray",
"darkgreen",
"darkgrey",
"darkkhaki",
"darkmagenta",
"darkolivegreen",
"darkorange",
"darkorchid",
"darkred",
"darksalmon",
"darkseagreen",
"darkslateblue",
"darkslategray",
"darkslategrey",
"darkturquoise",
"darkviolet",
"deeppink",
"deepskyblue",
"dimgray",
"dimgrey",
"dodgerblue",
"firebrick",
"floralwhite",
"forestgreen",
"fuchsia",
"gainsboro",
"ghostwhite",
"gold",
"goldenrod",
"gray",
"grey",
"green",
"greenyellow",
"honeydew",
"hotpink",
"indianred",
"indigo",
"ivory",
"khaki",
"lavender",
"lavenderblush",
"lawngreen",
"lemonchiffon",
"lightblue",
"lightcoral",
"lightcyan",
"lightgoldenrodyellow",
"lightgray",
"lightgreen",
"lightgrey",
"lightpink",
"lightsalmon",
"lightseagreen",
"lightskyblue",
"lightslategray",
"lightslategrey",
"lightsteelblue",
"lightyellow",
"lime",
"limegreen",
"linen",
"magenta",
"maroon",
"mediumaquamarine",
"mediumblue",
"mediumorchid",
"mediumpurple",
"mediumseagreen",
"mediumslateblue",
"mediumspringgreen",
"mediumturquoise",
"mediumvioletred",
"midnightblue",
"mintcream",
"mistyrose",
"moccasin",
"navajowhite",
"navy",
"oldlace",
"olive",
"olivedrab",
"orange",
"orangered",
"orchid",
"palegoldenrod",
"palegreen",
"paleturquoise",
"palevioletred",
"papayawhip",
"peachpuff",
"peru",
"pink",
"plum",
"powderblue",
"purple",
"red",
"rosybrown",
"royalblue",
"saddlebrown",
"salmon",
"sandybrown",
"seagreen",
"seashell",
"sienna",
"silver",
"skyblue",
"slateblue",
"slategray",
"slategrey",
"snow",
"springgreen",
"steelblue",
"tan",
"teal",
"thistle",
"tomato",
"turquoise",
"violet",
"wheat",
"white",
"whitesmoke",
"yellow",
"yellowgreen"
]
},
{
"title": "transparent",
"enum": ["transparent"]
}
]
},
"tab-colors": {
"type": "object",
"additionalProperties": false,
"properties": {
"backgrounds": {
"type": "object",
"additionalProperties": false,
"properties": {
"hover": { "$ref": "#/definitions/qt-color" },
"regular": { "$ref": "#/definitions/qt-color" },
"unfocused": { "$ref": "#/definitions/qt-color" }
},
"required": ["hover", "regular", "unfocused"]
},
"line": {
"type": "object",
"additionalProperties": false,
"properties": {
"hover": { "$ref": "#/definitions/qt-color" },
"regular": { "$ref": "#/definitions/qt-color" },
"unfocused": { "$ref": "#/definitions/qt-color" }
},
"required": ["hover", "regular", "unfocused"]
},
"text": { "$ref": "#/definitions/qt-color" }
},
"required": ["backgrounds", "line", "text"]
}
},
"type": "object",
"additionalProperties": false,
"properties": {
"colors": {
"type": "object",
"additionalProperties": false,
"properties": {
"accent": { "$ref": "#/definitions/qt-color" },
"messages": {
"type": "object",
"additionalProperties": false,
"properties": {
"backgrounds": {
"type": "object",
"additionalProperties": false,
"properties": {
"alternate": { "$ref": "#/definitions/qt-color" },
"regular": { "$ref": "#/definitions/qt-color" }
},
"required": ["alternate", "regular"]
},
"disabled": { "$ref": "#/definitions/qt-color" },
"highlightAnimationEnd": { "$ref": "#/definitions/qt-color" },
"highlightAnimationStart": { "$ref": "#/definitions/qt-color" },
"selection": { "$ref": "#/definitions/qt-color" },
"textColors": {
"type": "object",
"additionalProperties": false,
"properties": {
"caret": { "$ref": "#/definitions/qt-color" },
"chatPlaceholder": { "$ref": "#/definitions/qt-color" },
"link": { "$ref": "#/definitions/qt-color" },
"regular": { "$ref": "#/definitions/qt-color" },
"system": { "$ref": "#/definitions/qt-color" }
},
"required": [
"caret",
"chatPlaceholder",
"link",
"regular",
"system"
]
}
},
"required": [
"backgrounds",
"disabled",
"highlightAnimationEnd",
"highlightAnimationStart",
"selection",
"textColors"
]
},
"scrollbars": {
"type": "object",
"additionalProperties": false,
"properties": {
"background": { "$ref": "#/definitions/qt-color" },
"thumb": { "$ref": "#/definitions/qt-color" },
"thumbSelected": { "$ref": "#/definitions/qt-color" }
},
"required": ["background", "thumb", "thumbSelected"]
},
"splits": {
"type": "object",
"additionalProperties": false,
"properties": {
"background": { "$ref": "#/definitions/qt-color" },
"dropPreview": { "$ref": "#/definitions/qt-color" },
"dropPreviewBorder": { "$ref": "#/definitions/qt-color" },
"dropTargetRect": { "$ref": "#/definitions/qt-color" },
"dropTargetRectBorder": { "$ref": "#/definitions/qt-color" },
"header": {
"type": "object",
"additionalProperties": false,
"properties": {
"background": { "$ref": "#/definitions/qt-color" },
"border": { "$ref": "#/definitions/qt-color" },
"focusedBackground": { "$ref": "#/definitions/qt-color" },
"focusedBorder": { "$ref": "#/definitions/qt-color" },
"focusedText": { "$ref": "#/definitions/qt-color" },
"text": { "$ref": "#/definitions/qt-color" }
},
"required": [
"background",
"border",
"focusedBackground",
"focusedBorder",
"focusedText",
"text"
]
},
"input": {
"type": "object",
"additionalProperties": false,
"properties": {
"background": { "$ref": "#/definitions/qt-color" },
"text": { "$ref": "#/definitions/qt-color" }
},
"required": ["background", "text"]
},
"messageSeperator": { "$ref": "#/definitions/qt-color" },
"resizeHandle": { "$ref": "#/definitions/qt-color" },
"resizeHandleBackground": { "$ref": "#/definitions/qt-color" }
},
"required": [
"background",
"dropPreview",
"dropPreviewBorder",
"dropTargetRect",
"dropTargetRectBorder",
"header",
"input",
"messageSeperator",
"resizeHandle",
"resizeHandleBackground"
]
},
"tabs": {
"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"
},
"newMessage": {
"$ref": "#/definitions/tab-colors"
},
"regular": {
"$ref": "#/definitions/tab-colors"
},
"selected": {
"$ref": "#/definitions/tab-colors"
}
},
"required": [
"dividerLine",
"highlighted",
"newMessage",
"regular",
"selected"
]
},
"window": {
"type": "object",
"additionalProperties": false,
"properties": {
"background": { "$ref": "#/definitions/qt-color" },
"text": { "$ref": "#/definitions/qt-color" }
},
"required": ["background", "text"]
}
},
"required": [
"accent",
"messages",
"scrollbars",
"splits",
"tabs",
"window"
]
},
"metadata": {
"type": "object",
"additionalProperties": false,
"properties": {
"iconTheme": {
"$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"]
},
"$schema": { "type": "string" }
},
"required": ["colors", "metadata"]
}

133
docs/chatterino.d.ts vendored Normal file
View file

@ -0,0 +1,133 @@
/** @noSelfInFile */
declare module c2 {
enum LogLevel {
Debug,
Info,
Warning,
Critical,
}
class CommandContext {
words: String[];
channel: Channel;
}
enum Platform {
Twitch,
}
enum ChannelType {
None,
Direct,
Twitch,
TwitchWhispers,
TwitchWatching,
TwitchMentions,
TwitchLive,
TwitchAutomod,
Irc,
Misc,
}
interface IWeakResource {
is_valid(): boolean;
}
interface ISharedResource {}
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;
}
enum HTTPMethod {
Get,
Post,
Put,
Delete,
Patch,
}
class HTTPResponse implements ISharedResource {
data(): string;
status(): number | null;
error(): string;
}
type HTTPCallback = (res: HTTPResponse) => void;
class HTTPRequest implements ISharedResource {
on_success(callback: HTTPCallback): void;
on_error(callback: HTTPCallback): void;
finally(callback: () => void): void;
set_timeout(millis: number): void;
set_payload(data: string): void;
set_header(name: string, value: string): void;
execute(): void;
// might error
static create(method: HTTPMethod, url: string): HTTPRequest;
}
function log(level: LogLevel, ...data: any[]): void;
function register_command(
name: String,
handler: (ctx: CommandContext) => void
): boolean;
class CompletionEvent {
query: string;
full_text_content: string;
cursor_position: number;
is_first_word: boolean;
}
class CompletionList {
values: String[];
hide_others: boolean;
}
enum EventType {
CompletionRequested = "CompletionRequested",
}
type CbFuncCompletionsRequested = (ev: CompletionEvent) => 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

@ -1,4 +1,25 @@
# Checklist for making a release
## In the release PR
- [ ] Updated version code in `src/common/Version.hpp`
- [ ] Updated version code in `CMakeLists.txt`
- [ ] 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 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`
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`
- [ ] Update the changelog `## Unreleased` section to the new version `CHANGELOG.md`
Make sure to leave the `## Unreleased` line unchanged for easier merges
## After the PR has been merged
- [ ] Tag the release
- [ ] Manually run the [create-installer](https://github.com/Chatterino/chatterino2/actions/workflows/create-installer.yml) workflow.
This is only necessary if the tag was created after the CI in the main branch finished.
- [ ] Start a manual [Launchpad import](https://code.launchpad.net/~pajlada/chatterino/+git/chatterino) - scroll down & click Import Now
- [ ] Make a PPA release to [this repo](https://git.launchpad.net/~pajlada/+git/chatterino-packaging/) with the `debchange` command.
`debchange -v 2.4.0` then add the changelog entries
`debchange --release` then change the distro to be `unstable`

View file

@ -0,0 +1,62 @@
{
"$schema": "http://json-schema.org/schema",
"$id": "https://raw.githubusercontent.com/Chatterino/chatterino2/master/docs/plugin-info.schema.json",
"title": "Plugin info",
"description": "Describes a Chatterino2 plugin (draft)",
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"description": "Plugin name shown to the user."
},
"description": {
"type": "string",
"description": "Plugin description shown to the user."
},
"authors": {
"type": "array",
"description": "An array of authors of this Plugin.",
"items": {
"type": "string"
}
},
"homepage": {
"type": "string",
"description": "Optional URL to your Plugin's homepage. This could be your GitHub repo for example."
},
"tags": {
"description": "Something that could in the future be used to find your plugin.",
"type": "array",
"items": {
"type": "string",
"examples": ["moderation", "utility", "commands"]
},
"uniqueItems": true
},
"version": {
"type": "string",
"description": "Semver version string, for more info see https://semver.org.",
"examples": ["0.0.1", "1.0.0-rc.1"]
},
"license": {
"type": "string",
"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", "Network"]
}
}
}
},
"$schema": { "type": "string" }
},
"required": ["name", "description", "authors", "version", "license"]
}

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

@ -0,0 +1,263 @@
---@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 = {}
---@alias c2.LogLevel integer
---@type { Debug: c2.LogLevel, Info: c2.LogLevel, Warning: c2.LogLevel, Critical: c2.LogLevel }
c2.LogLevel = {}
---@alias c2.EventType integer
---@type { CompletionRequested: c2.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 c2.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.
---@class CompletionEvent
---@field query string The word being completed
---@field full_text_content string Content of the text input
---@field cursor_position integer Position of the cursor in the text input in unicode codepoints (not bytes)
---@field is_first_word boolean True if this is the first word in the input
-- Begin src/common/Channel.hpp
---@alias c2.ChannelType integer
---@type { None: c2.ChannelType, Direct: c2.ChannelType, Twitch: c2.ChannelType, TwitchWhispers: c2.ChannelType, TwitchWatching: c2.ChannelType, TwitchMentions: c2.ChannelType, TwitchLive: c2.ChannelType, TwitchAutomod: c2.ChannelType, TwitchEnd: c2.ChannelType, Irc: c2.ChannelType, Misc: c2.ChannelType }
c2.ChannelType = {}
-- End src/common/Channel.hpp
-- Begin src/controllers/plugins/api/ChannelRef.hpp
---@alias c2.Platform integer
--- This enum describes a platform for the purpose of searching for a channel.
--- Currently only Twitch is supported because identifying IRC channels is tricky.
---@type { Twitch: c2.Platform }
c2.Platform = {}
---@class c2.Channel
c2.Channel = {}
--- 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 c2.Channel:is_valid() end
--- Gets the channel's name. This is the lowercase login name.
---
---@return string name
function c2.Channel:get_name() end
--- Gets the channel's type
---
---@return c2.ChannelType
function c2.Channel:get_type() end
--- Get the channel owner's display name. This may contain non-lowercase ascii characters.
---
---@return string name
function c2.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 c2.Channel:send_message(message, execute_commands) end
--- Adds a system message client-side
---
---@param message string
function c2.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 boolean
function c2.Channel:is_twitch_channel() end
--- Returns a copy of the channel mode settings (subscriber only, r9k etc.)
---
---@return RoomModes
function c2.Channel:get_room_modes() end
--- Returns a copy of the stream status.
---
---@return StreamStatus
function c2.Channel:get_stream_status() end
--- Returns the Twitch user ID of the owner of the channel.
---
---@return string
function c2.Channel:get_twitch_id() end
--- Returns true if the channel is a Twitch channel and the user owns it
---
---@return boolean
function c2.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 c2.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 c2.Channel:is_vip() end
---@return string
function c2.Channel:__tostring() end
--- 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 c2.Platform Where to search for the channel?
---@return c2.Channel?
function c2.Channel.by_name(name, platform) end
--- Finds a channel by the Twitch user ID of its owner.
---
---@param id string ID of the owner of the channel.
---@return c2.Channel?
function c2.Channel.by_twitch_id(id) 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 follower_only 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
-- End src/controllers/plugins/api/ChannelRef.hpp
-- Begin src/controllers/plugins/api/HTTPResponse.hpp
---@class HTTPResponse
HTTPResponse = {}
--- Returns the data. This is not guaranteed to be encoded using any
--- particular encoding scheme. It's just the bytes the server returned.
---
function HTTPResponse:data() end
--- Returns the status code.
---
function HTTPResponse:status() end
--- A somewhat human readable description of an error if such happened
---
function HTTPResponse:error() end
-- End src/controllers/plugins/api/HTTPResponse.hpp
-- Begin src/controllers/plugins/api/HTTPRequest.hpp
---@alias HTTPCallback fun(result: HTTPResponse): nil
---@class HTTPRequest
HTTPRequest = {}
--- Sets the success callback
---
---@param callback HTTPCallback Function to call when the HTTP request succeeds
function HTTPRequest:on_success(callback) end
--- Sets the failure callback
---
---@param callback HTTPCallback Function to call when the HTTP request fails or returns a non-ok status
function HTTPRequest:on_error(callback) end
--- Sets the finally callback
---
---@param callback fun(): nil Function to call when the HTTP request finishes
function HTTPRequest:finally(callback) end
--- Sets the timeout
---
---@param timeout integer How long in milliseconds until the times out
function HTTPRequest:set_timeout(timeout) end
--- Sets the request payload
---
---@param data string
function HTTPRequest:set_payload(data) end
--- Sets a header in the request
---
---@param name string
---@param value string
function HTTPRequest:set_header(name, value) end
--- Executes the HTTP request
---
function HTTPRequest:execute() end
--- Creates a new HTTPRequest
---
---@param method HTTPMethod Method to use
---@param url string Where to send the request to
---@return HTTPRequest
function HTTPRequest.create(method, url) end
-- End src/controllers/plugins/api/HTTPRequest.hpp
-- Begin src/common/network/NetworkCommon.hpp
---@alias HTTPMethod integer
---@type { Get: HTTPMethod, Post: HTTPMethod, Put: HTTPMethod, Delete: HTTPMethod, Patch: HTTPMethod }
HTTPMethod = {}
-- End src/common/network/NetworkCommon.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(event: CompletionEvent): CompletionList The callback to be invoked.
function c2.register_callback(type, func) end
--- Writes a message to the Chatterino log.
---
---@param level c2.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

661
docs/wip-plugins.md Normal file
View file

@ -0,0 +1,661 @@
# Plugins
If Chatterino is compiled with the `CHATTERINO_PLUGINS` CMake option, it can
load and execute Lua files. Note that while there are attempts at making this
decently safe, we cannot guarantee safety.
## Plugin structure
Chatterino searches for plugins in the `Plugins` directory in the app data, right next to `Settings` and `Logs`.
Each plugin should have its own directory.
```
Chatterino Plugins dir/
└── plugin_name/
├── init.lua
├── 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 [`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**
be [semver 2.0](https://semver.org/) compliant. The general idea of `info.json`
will not change however the exact contents probably will, for example with
permission system ideas.
Example file:
```json
{
"$schema": "https://raw.githubusercontent.com/Chatterino/chatterino2/master/docs/plugin-info.schema.json",
"name": "Test plugin",
"description": "This plugin is for testing stuff.",
"authors": ["Mm2PL"],
"homepage": "https://github.com/Chatterino/Chatterino2",
"tags": ["test"],
"version": "0.0.0",
"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"
},
...
]
}
```
### Network
Allows the plugin to send HTTP requests.
Example:
```json
{
...,
"permissions": [
{
"type": "Network"
},
...
]
}
```
## 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
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)
- `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).
### Chatterino API
All Chatterino functions are exposed in a global table called `c2`. The following members are available:
#### `log(level, args...)`
Writes a message to the Chatterino log. The `level` argument should be a
`LogLevel` member. All `args` should be convertible to a string with
`tostring()`.
Example:
```lua
c2.log(c2.LogLevel.Warning, "Hello, this should show up in the Chatterino log by default")
c2.log(c2.LogLevel.Debug, "Hello world")
-- Equivalent to doing qCDebug(chatterinoLua) << "[pluginDirectory:Plugin Name]" << "Hello, world"; from C++
```
#### `LogLevel` enum
This table describes log levels available to Lua Plugins. The values behind the names may change, do not count on them. It has the following keys:
- `Debug`
- `Info`
- `Warning`
- `Critical`
#### `register_command(name, handler)`
Registers a new command called `name` which when executed will call `handler`.
Returns `true` if everything went ok, `false` if there already exists another
command with this name.
Example:
```lua
function cmd_words(ctx)
-- ctx contains:
-- words - table of words supplied to the command including the trigger
-- channel - the channel the command is being run in
channel:add_system_message("Words are: " .. table.concat(ctx.words, " "))
end
c2.register_command("/words", cmd_words)
```
Limitations/known issues:
- Commands registered in functions, not in the global scope might not show up in the settings UI,
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).
#### `register_callback("CompletionRequested", handler)`
Registers a callback (`handler`) to process completions. The callback takes a single table with the following entries:
- `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(event)
if ("!join"):startswith(event.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
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, " ")
ctx.channel:send_message(string.upper(output))
end
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.
##### `Channel:add_system_message(message)`
Shows a system message in the channel with the given text.
Example:
```lua
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.
#### `HTTPMethod` enum
This table describes HTTP methods available to Lua Plugins. The values behind
the names may change, do not count on them. It has the following keys:
- `Get`
- `Post`
- `Put`
- `Delete`
- `Patch`
#### `HTTPResponse`
An `HTTPResponse` is a table you receive in the callback after a completed `HTTPRequest`.
##### `HTTPResponse.data()`
This function returns the data received from the server as a string. Usually
this will be UTF-8-encoded however that is not guaranteed, this could be any
binary data.
##### `HTTPResponse.error()`
If an error happened this function returns a human readable description of it.
It returns something like: `"ConnectionRefusedError"`, `"401"`.
##### `HTTPResponse.status()`
This function returns the HTTP status code of the request or `nil` if there was
an error before a status code could be received.
#### `HTTPRequest`
Allows you to send an HTTP request to a URL. Do not create requests that you
don't want to call `execute()` on. For the time being that leaks callback
functions and all their upvalues with them.
##### `HTTPRequest.create(method, url)`
Creates a new `HTTPRequest`. The `method` argument is an
[`HTTPMethod`](#HTTPMethod-enum). The `url` argument must be a string
containing a valid URL (ex. `https://example.com/path/to/api`).
```lua
local req = c2.HTTPRequest.create(c2.HTTPMethod.Get, "https://example.com")
req:on_success(function (res)
print(res:data())
end)
req:execute()
```
##### `HTTPRequest:on_success(callback)`
Sets the success callback. It accepts a function that takes a single parameter
of type `HTTPResponse`. The callback will be called on success. This function
returns nothing.
##### `HTTPRequest:on_error(callback)`
Sets the error callback. It accepts a function that takes a single parameter of
type `HTTPResponse`. The callback will be called if the request fails. To see why
it failed check the `error` field of the result. This function returns nothing.
##### `HTTPRequest:finally(callback)`
Sets the finally callback. It accepts a function that takes no parameters and
returns nothing. It will be always called after `success` or `error`. This
function returns nothing.
##### `HTTPRequest:set_timeout(timeout)`
Sets how long the request will take before it times out. The `timeout`
parameter is in milliseconds. This function returns nothing.
##### `HTTPRequest:set_payload(data)`
Sets the data that will be sent with the request. The `data` parameter must be
a string. This function returns nothing.
##### `HTTPRequest:set_header(name, value)`
Adds or overwrites a header in the request. Both `name` and `value` should be
strings. If they are not strings they will be converted to strings. This
function returns nothing.
##### `HTTPRequest:execute()`
Sends the request. This function returns nothing.
```lua
local url = "http://localhost:8080/thing"
local request = c2.HTTPRequest.create("Post", url)
request:set_timeout(1000)
request:set_payload("TEST!")
request:set_header("X-Test", "Testing!")
request:set_header("Content-Type", "text/plain")
request:on_success(function (res)
print('Success!')
-- Data is in res.data
print(res:status())
end)
request:on_error(function (res)
print('Error!')
print(res:status())
print(res:error())
end)
request:finally(function ()
print('Finally')
end)
request:execute()
-- This prints:
-- Success!
-- [content of /thing]
-- 200
-- Finally
-- Or:
-- Error!
-- nil
-- ConnectionRefusedError
```
### 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]]])`
This function is only available if Chatterino is compiled in debug mode. It is meant for debugging with little exception.
This function behaves really similarity to Lua's `load`, however it does not allow for bytecode to be executed.
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)
#### `require(modname)`
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
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...)`
The `print` global function is equivalent to calling `c2.log(c2.LogLevel.Debug, 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 +1 @@
Subproject commit 5e441fd03543b999edb663caf8df7be37c0d575c
Subproject commit a78ce469b456c06103b3b30d4bd37e7bb80da30c

1
lib/expected-lite Submodule

@ -0,0 +1 @@
Subproject commit f339d2f73730f8fee4412f5e4938717866ecef48

@ -1 +1 @@
Subproject commit a7b32cd6fa0640721b6114b31d37d79ebf832411
Subproject commit 030710ad53dda1541601ccabbad36a12a9e90c78

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