├── .clang-format ├── .cmake-format.json ├── .github ├── FUNDING.yml ├── actions │ ├── build-plugin │ │ └── action.yaml │ ├── package-plugin │ │ └── action.yaml │ ├── run-clang-format │ │ └── action.yaml │ ├── run-cmake-format │ │ └── action.yaml │ └── setup-macos-codesigning │ │ └── action.yaml ├── scripts │ ├── .Aptfile │ ├── .Brewfile │ ├── .Wingetfile │ ├── .build.zsh │ ├── .package.zsh │ ├── Build-Windows.ps1 │ ├── Package-Windows.ps1 │ ├── build-linux │ ├── build-macos │ ├── package-linux │ ├── package-macos │ ├── utils.pwsh │ │ ├── Ensure-Location.ps1 │ │ ├── Expand-ArchiveExt.ps1 │ │ ├── Install-BuildDependencies.ps1 │ │ ├── Invoke-External.ps1 │ │ └── Logger.ps1 │ └── utils.zsh │ │ ├── check_linux │ │ ├── check_macos │ │ ├── log_debug │ │ ├── log_error │ │ ├── log_group │ │ ├── log_info │ │ ├── log_output │ │ ├── log_status │ │ ├── log_warning │ │ ├── mkcd │ │ ├── read_codesign │ │ ├── read_codesign_installer │ │ ├── read_codesign_pass │ │ ├── read_codesign_team │ │ ├── read_codesign_user │ │ ├── set_loglevel │ │ ├── setup_ccache │ │ └── setup_linux └── workflows │ ├── build-project.yaml │ ├── check-format.yaml │ ├── dispatch.yaml │ ├── pr-pull.yaml │ └── push.yaml ├── .gitignore ├── CMakeLists.txt ├── CMakePresets.json ├── LICENSE ├── README.md ├── README_ja.md ├── build-aux ├── .functions │ ├── log_debug │ ├── log_error │ ├── log_group │ ├── log_info │ ├── log_output │ ├── log_status │ ├── log_warning │ └── set_loglevel ├── .run-format.zsh ├── run-clang-format ├── run-cmake-format └── run-swift-format ├── build-windows-installer.ps1 ├── buildspec.json ├── cmake ├── common │ ├── bootstrap.cmake │ ├── buildnumber.cmake │ ├── buildspec_common.cmake │ ├── ccache.cmake │ ├── compiler_common.cmake │ ├── helpers_common.cmake │ └── osconfig.cmake ├── linux │ ├── compilerconfig.cmake │ ├── defaults.cmake │ ├── helpers.cmake │ └── toolchains │ │ ├── aarch64-linux-clang.cmake │ │ ├── aarch64-linux-gcc.cmake │ │ ├── x86_64-linux-clang.cmake │ │ └── x86_64-linux-gcc.cmake ├── macos │ ├── buildspec.cmake │ ├── compilerconfig.cmake │ ├── defaults.cmake │ ├── helpers.cmake │ ├── resources │ │ ├── ccache-launcher-c.in │ │ ├── ccache-launcher-cxx.in │ │ ├── create-package.cmake.in │ │ ├── distribution.in │ │ └── installer-macos.pkgproj.in │ └── xcode.cmake └── windows │ ├── buildspec.cmake │ ├── compilerconfig.cmake │ ├── defaults.cmake │ ├── helpers.cmake │ └── resources │ ├── installer-Windows.iss.in │ └── resource.rc.in ├── data └── locale │ ├── ca-ES.ini │ ├── de-DE.ini │ ├── en-US.ini │ ├── fr-FR.ini │ ├── ja-JP.ini │ ├── ko-KR.ini │ ├── ro-RO.ini │ ├── ru-RU.ini │ ├── uk-UA.ini │ └── zh-CN.ini ├── screenshot1.jpg ├── screenshot2.jpg └── src ├── UI ├── images │ ├── chapter.svg │ ├── invisible.svg │ ├── pause.svg │ ├── recording-paused.svg │ ├── recording.svg │ ├── scissors.svg │ ├── streaming.svg │ ├── unpause.svg │ └── visible.svg ├── output-status-dock.cpp ├── output-status-dock.hpp └── resources.qrc ├── audio ├── audio-capture.cpp └── audio-capture.hpp ├── plugin-main.cpp ├── plugin-main.hpp ├── plugin-support.c.in ├── plugin-support.h ├── plugin-ui.cpp ├── utils.cpp └── utils.hpp /.clang-format: -------------------------------------------------------------------------------- 1 | # please use clang-format version 16 or later 2 | 3 | Standard: c++17 4 | AccessModifierOffset: -4 5 | AlignAfterOpenBracket: BlockIndent 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Left 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllArgumentsOnNextLine: false 12 | AllowAllConstructorInitializersOnNextLine: false 13 | AllowAllParametersOfDeclarationOnNextLine: false 14 | AllowShortBlocksOnASingleLine: false 15 | AllowShortCaseLabelsOnASingleLine: false 16 | AllowShortFunctionsOnASingleLine: Inline 17 | AllowShortIfStatementsOnASingleLine: false 18 | AllowShortLambdasOnASingleLine: Inline 19 | AllowShortLoopsOnASingleLine: false 20 | AlwaysBreakAfterDefinitionReturnType: None 21 | AlwaysBreakAfterReturnType: None 22 | AlwaysBreakBeforeMultilineStrings: false 23 | AlwaysBreakTemplateDeclarations: false 24 | BinPackArguments: true 25 | BinPackParameters: true 26 | BraceWrapping: 27 | AfterCaseLabel: false 28 | AfterClass: false 29 | AfterControlStatement: false 30 | AfterEnum: false 31 | AfterFunction: true 32 | AfterNamespace: false 33 | AfterObjCDeclaration: false 34 | AfterStruct: false 35 | AfterUnion: false 36 | AfterExternBlock: false 37 | BeforeCatch: false 38 | BeforeElse: false 39 | IndentBraces: false 40 | SplitEmptyFunction: true 41 | SplitEmptyRecord: true 42 | SplitEmptyNamespace: true 43 | BreakBeforeBinaryOperators: None 44 | BreakBeforeBraces: Custom 45 | BreakBeforeTernaryOperators: true 46 | BreakConstructorInitializers: BeforeColon 47 | BreakStringLiterals: false # apparently unpredictable 48 | ColumnLimit: 120 49 | CompactNamespaces: false 50 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 51 | ConstructorInitializerIndentWidth: 4 52 | ContinuationIndentWidth: 4 53 | Cpp11BracedListStyle: true 54 | DerivePointerAlignment: false 55 | DisableFormat: false 56 | FixNamespaceComments: true 57 | ForEachMacros: 58 | - 'json_object_foreach' 59 | - 'json_object_foreach_safe' 60 | - 'json_array_foreach' 61 | - 'HASH_ITER' 62 | - 'foreach' 63 | IndentAccessModifiers: false 64 | IncludeBlocks: Preserve 65 | IndentCaseLabels: false 66 | IndentPPDirectives: None 67 | IndentWidth: 4 68 | IndentWrappedFunctionNames: false 69 | KeepEmptyLinesAtTheStartOfBlocks: true 70 | MaxEmptyLinesToKeep: 1 71 | NamespaceIndentation: None 72 | ObjCBinPackProtocolList: Auto 73 | ObjCBlockIndentWidth: 4 74 | ObjCSpaceAfterProperty: true 75 | ObjCSpaceBeforeProtocolList: true 76 | 77 | PenaltyBreakAssignment: 10 78 | PenaltyBreakBeforeFirstCallParameter: 30 79 | PenaltyBreakComment: 10 80 | PenaltyBreakFirstLessLess: 0 81 | PenaltyBreakString: 10 82 | PenaltyExcessCharacter: 100 83 | PenaltyReturnTypeOnItsOwnLine: 60 84 | 85 | PointerAlignment: Right 86 | ReflowComments: false 87 | SortIncludes: false 88 | SortUsingDeclarations: false 89 | SpaceAfterCStyleCast: false 90 | SpaceAfterLogicalNot: false 91 | SpaceAfterTemplateKeyword: false 92 | SpaceBeforeAssignmentOperators: true 93 | SpaceBeforeCtorInitializerColon: true 94 | SpaceBeforeInheritanceColon: true 95 | SpaceBeforeParens: ControlStatements 96 | SpaceBeforeRangeBasedForLoopColon: true 97 | SpaceInEmptyParentheses: false 98 | SpacesBeforeTrailingComments: 1 99 | SpacesInAngles: false 100 | SpacesInCStyleCastParentheses: false 101 | SpacesInContainerLiterals: false 102 | SpacesInParentheses: false 103 | SpacesInSquareBrackets: false 104 | StatementMacros: 105 | - 'Q_OBJECT' 106 | TabWidth: 4 107 | TypenameMacros: 108 | - 'DARRAY' 109 | UseTab: Never 110 | --- 111 | Language: ObjC 112 | AccessModifierOffset: -4 113 | AlignArrayOfStructures: Right 114 | AlignConsecutiveAssignments: None 115 | AlignConsecutiveBitFields: None 116 | AlignConsecutiveDeclarations: None 117 | AlignConsecutiveMacros: 118 | Enabled: true 119 | AcrossEmptyLines: false 120 | AcrossComments: true 121 | AllowShortBlocksOnASingleLine: Never 122 | AllowShortEnumsOnASingleLine: false 123 | AllowShortFunctionsOnASingleLine: Empty 124 | AllowShortIfStatementsOnASingleLine: Never 125 | AllowShortLambdasOnASingleLine: None 126 | AttributeMacros: ['__unused', '__autoreleasing', '_Nonnull', '__bridge'] 127 | BitFieldColonSpacing: Both 128 | #BreakBeforeBraces: Webkit 129 | BreakBeforeBraces: Custom 130 | BraceWrapping: 131 | AfterCaseLabel: true 132 | AfterClass: true 133 | AfterControlStatement: Never 134 | AfterEnum: false 135 | AfterFunction: true 136 | AfterNamespace: false 137 | AfterObjCDeclaration: false 138 | AfterStruct: false 139 | AfterUnion: false 140 | AfterExternBlock: false 141 | BeforeCatch: false 142 | BeforeElse: false 143 | BeforeLambdaBody: false 144 | BeforeWhile: false 145 | IndentBraces: false 146 | SplitEmptyFunction: false 147 | SplitEmptyRecord: false 148 | SplitEmptyNamespace: true 149 | BreakAfterAttributes: Never 150 | BreakArrays: false 151 | BreakBeforeConceptDeclarations: Allowed 152 | BreakBeforeInlineASMColon: OnlyMultiline 153 | BreakConstructorInitializers: AfterColon 154 | BreakInheritanceList: AfterComma 155 | ColumnLimit: 120 156 | ConstructorInitializerIndentWidth: 4 157 | ContinuationIndentWidth: 4 158 | EmptyLineAfterAccessModifier: Never 159 | EmptyLineBeforeAccessModifier: LogicalBlock 160 | ExperimentalAutoDetectBinPacking: false 161 | FixNamespaceComments: true 162 | IndentAccessModifiers: false 163 | IndentCaseBlocks: false 164 | IndentCaseLabels: true 165 | IndentExternBlock: Indent 166 | IndentGotoLabels: false 167 | IndentRequiresClause: true 168 | IndentWidth: 4 169 | IndentWrappedFunctionNames: true 170 | InsertBraces: false 171 | InsertNewlineAtEOF: true 172 | KeepEmptyLinesAtTheStartOfBlocks: false 173 | LambdaBodyIndentation: Signature 174 | NamespaceIndentation: All 175 | ObjCBinPackProtocolList: Auto 176 | ObjCBlockIndentWidth: 4 177 | ObjCBreakBeforeNestedBlockParam: false 178 | ObjCSpaceAfterProperty: true 179 | ObjCSpaceBeforeProtocolList: true 180 | PPIndentWidth: -1 181 | PackConstructorInitializers: NextLine 182 | QualifierAlignment: Leave 183 | ReferenceAlignment: Right 184 | RemoveSemicolon: false 185 | RequiresClausePosition: WithPreceding 186 | RequiresExpressionIndentation: OuterScope 187 | SeparateDefinitionBlocks: Always 188 | ShortNamespaceLines: 1 189 | SortIncludes: false 190 | #SortUsingDeclarations: LexicographicNumeric 191 | SortUsingDeclarations: true 192 | SpaceAfterCStyleCast: true 193 | SpaceAfterLogicalNot: false 194 | SpaceAroundPointerQualifiers: Default 195 | SpaceBeforeCaseColon: false 196 | SpaceBeforeCpp11BracedList: true 197 | SpaceBeforeCtorInitializerColon: true 198 | SpaceBeforeInheritanceColon: true 199 | SpaceBeforeParens: ControlStatements 200 | SpaceBeforeRangeBasedForLoopColon: true 201 | SpaceBeforeSquareBrackets: false 202 | SpaceInEmptyBlock: false 203 | SpaceInEmptyParentheses: false 204 | SpacesBeforeTrailingComments: 2 205 | SpacesInConditionalStatement: false 206 | SpacesInLineCommentPrefix: 207 | Minimum: 1 208 | Maximum: -1 209 | Standard: c++17 210 | TabWidth: 4 211 | UseTab: Never 212 | -------------------------------------------------------------------------------- /.cmake-format.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": { 3 | "line_width": 120, 4 | "tab_size": 2, 5 | "enable_sort": true, 6 | "autosort": true 7 | }, 8 | "additional_commands": { 9 | "set_target_properties_obs": { 10 | "pargs": 1, 11 | "flags": [], 12 | "kwargs": { 13 | "PROPERTIES": { 14 | "kwargs": { 15 | "PREFIX": 1, 16 | "OUTPUT_NAME": 1, 17 | "FOLDER": 1, 18 | "VERSION": 1, 19 | "SOVERSION": 1, 20 | "AUTOMOC": 1, 21 | "AUTOUIC": 1, 22 | "AUTORCC": 1, 23 | "AUTOUIC_SEARCH_PATHS": 1, 24 | "BUILD_RPATH": 1, 25 | "INSTALL_RPATH": 1 26 | } 27 | } 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [OPENSPHERE-Inc] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.github/actions/build-plugin/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Set up and build plugin' 2 | description: 'Builds the plugin for specified architecture and build config' 3 | inputs: 4 | target: 5 | description: 'Target architecture for dependencies' 6 | required: true 7 | config: 8 | description: 'Build configuration' 9 | required: false 10 | default: 'RelWithDebInfo' 11 | codesign: 12 | description: 'Enable codesigning (macOS only)' 13 | required: false 14 | default: 'false' 15 | codesignIdent: 16 | description: 'Developer ID for application codesigning (macOS only)' 17 | required: false 18 | default: '-' 19 | workingDirectory: 20 | description: 'Working directory for packaging' 21 | required: false 22 | default: ${{ github.workspace }} 23 | runs: 24 | using: composite 25 | steps: 26 | - name: Run macOS Build 27 | if: runner.os == 'macOS' 28 | shell: zsh --no-rcs --errexit --pipefail {0} 29 | working-directory: ${{ inputs.workingDirectory }} 30 | env: 31 | CODESIGN_IDENT: ${{ inputs.codesignIdent }} 32 | CODESIGN_TEAM: ${{ inputs.codesignTeam }} 33 | run: | 34 | : Run macOS Build 35 | 36 | local -a build_args=(--config ${{ inputs.config }}) 37 | if (( ${+RUNNER_DEBUG} )) build_args+=(--debug) 38 | 39 | if [[ '${{ inputs.codesign }}' == 'true' ]] build_args+=(--codesign) 40 | 41 | .github/scripts/build-macos ${build_args} 42 | 43 | - name: Install Dependencies 🛍️ 44 | if: runner.os == 'Linux' 45 | shell: bash 46 | run: | 47 | : Install Dependencies 🛍️ 48 | echo ::group::Install Dependencies 49 | eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" 50 | echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH 51 | brew install --quiet zsh 52 | echo ::endgroup:: 53 | 54 | - name: Run Ubuntu Build 55 | if: runner.os == 'Linux' 56 | shell: zsh --no-rcs --errexit --pipefail {0} 57 | working-directory: ${{ inputs.workingDirectory }} 58 | run: | 59 | : Run Ubuntu Build 60 | 61 | local -a build_args=( 62 | --target linux-${{ inputs.target }} 63 | --config ${{ inputs.config }} 64 | ) 65 | if (( ${+RUNNER_DEBUG} )) build_args+=(--debug) 66 | 67 | .github/scripts/build-linux ${build_args} 68 | 69 | - name: Run Windows Build 70 | if: runner.os == 'Windows' 71 | shell: pwsh 72 | run: | 73 | # Run Windows Build 74 | if ( $Env:RUNNER_DEBUG -ne $null ) { 75 | Set-PSDebug -Trace 1 76 | } 77 | 78 | $BuildArgs = @{ 79 | Target = '${{ inputs.target }}' 80 | Configuration = '${{ inputs.config }}' 81 | } 82 | 83 | .github/scripts/Build-Windows.ps1 @BuildArgs 84 | 85 | - name: Create Summary 📊 86 | if: contains(fromJSON('["Linux", "macOS"]'),runner.os) 87 | shell: zsh --no-rcs --errexit --pipefail {0} 88 | env: 89 | CCACHE_CONFIGPATH: ${{ inputs.workingDirectory }}/.ccache.conf 90 | run: | 91 | : Create Summary 📊 92 | 93 | local -a ccache_data 94 | if (( ${+RUNNER_DEBUG} )) { 95 | setopt XTRACE 96 | ccache_data=("${(fA)$(ccache -s -vv)}") 97 | } else { 98 | ccache_data=("${(fA)$(ccache -s)}") 99 | } 100 | 101 | print '### ${{ runner.os }} Ccache Stats (${{ inputs.target }})' >> $GITHUB_STEP_SUMMARY 102 | print '```' >> $GITHUB_STEP_SUMMARY 103 | for line (${ccache_data}) { 104 | print ${line} >> $GITHUB_STEP_SUMMARY 105 | } 106 | print '```' >> $GITHUB_STEP_SUMMARY 107 | -------------------------------------------------------------------------------- /.github/actions/package-plugin/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Package plugin' 2 | description: 'Packages the plugin for specified architecture and build config.' 3 | inputs: 4 | target: 5 | description: 'Build target for dependencies' 6 | required: true 7 | config: 8 | description: 'Build configuration' 9 | required: false 10 | default: 'RelWithDebInfo' 11 | codesign: 12 | description: 'Enable codesigning (macOS only)' 13 | required: false 14 | default: 'false' 15 | notarize: 16 | description: 'Enable notarization (macOS only)' 17 | required: false 18 | default: 'false' 19 | codesignIdent: 20 | description: 'Developer ID for application codesigning (macOS only)' 21 | required: false 22 | default: '-' 23 | installerIdent: 24 | description: 'Developer ID for installer package codesigning (macOS only)' 25 | required: false 26 | default: '' 27 | codesignTeam: 28 | description: 'Developer team for codesigning (macOS only)' 29 | required: false 30 | default: '' 31 | codesignUser: 32 | description: 'Apple ID username for notarization (macOS only)' 33 | required: false 34 | default: '' 35 | codesignPass: 36 | description: 'Apple ID password for notarization (macOS only)' 37 | required: false 38 | default: '' 39 | package: 40 | description: 'Create Windows or macOS installation package' 41 | required: false 42 | default: 'false' 43 | workingDirectory: 44 | description: 'Working directory for packaging' 45 | required: false 46 | default: ${{ github.workspace }} 47 | runs: 48 | using: composite 49 | steps: 50 | - name: Run macOS Packaging 51 | if: runner.os == 'macOS' 52 | shell: zsh --no-rcs --errexit --pipefail {0} 53 | working-directory: ${{ inputs.workingDirectory }} 54 | env: 55 | CODESIGN_IDENT: ${{ inputs.codesignIdent }} 56 | CODESIGN_IDENT_INSTALLER: ${{ inputs.installerIdent }} 57 | CODESIGN_TEAM: ${{ inputs.codesignTeam }} 58 | CODESIGN_IDENT_USER: ${{ inputs.codesignUser }} 59 | CODESIGN_IDENT_PASS: ${{ inputs.codesignPass }} 60 | run: | 61 | : Run macOS Packaging 62 | 63 | local -a package_args=(--config ${{ inputs.config }}) 64 | if (( ${+RUNNER_DEBUG} )) package_args+=(--debug) 65 | 66 | if [[ '${{ inputs.codesign }}' == 'true' ]] package_args+=(--codesign) 67 | if [[ '${{ inputs.notarize }}' == 'true' ]] package_args+=(--notarize) 68 | if [[ '${{ inputs.package }}' == 'true' ]] package_args+=(--package) 69 | 70 | .github/scripts/package-macos ${package_args} 71 | 72 | - name: Install Dependencies 🛍️ 73 | if: runner.os == 'Linux' 74 | shell: bash 75 | run: | 76 | : Install Dependencies 🛍️ 77 | echo ::group::Install Dependencies 78 | eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" 79 | echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH 80 | brew install --quiet zsh 81 | echo ::endgroup:: 82 | 83 | - name: Run Ubuntu Packaging 84 | if: runner.os == 'Linux' 85 | shell: zsh --no-rcs --errexit --pipefail {0} 86 | working-directory: ${{ inputs.workingDirectory }} 87 | run: | 88 | : Run Ubuntu Packaging 89 | package_args=( 90 | --target linux-${{ inputs.target }} 91 | --config ${{ inputs.config }} 92 | ) 93 | if (( ${+RUNNER_DEBUG} )) build_args+=(--debug) 94 | 95 | if [[ '${{ inputs.package }}' == 'true' ]] package_args+=(--package) 96 | 97 | .github/scripts/package-linux ${package_args} 98 | 99 | - name: Run Windows Packaging 100 | if: runner.os == 'Windows' 101 | shell: pwsh 102 | run: | 103 | # Run Windows Packaging 104 | if ( $Env:RUNNER_DEBUG -ne $null ) { 105 | Set-PSDebug -Trace 1 106 | } 107 | 108 | $PackageArgs = @{ 109 | Target = '${{ inputs.target }}' 110 | Configuration = '${{ inputs.config }}' 111 | } 112 | 113 | # Do not build installer package for windows at this time 114 | #if ( '${{ inputs.package }}' -eq 'true' ) { 115 | # $PackageArgs += @{BuildInstaller = $true} 116 | #} 117 | 118 | .github/scripts/Package-Windows.ps1 @PackageArgs 119 | -------------------------------------------------------------------------------- /.github/actions/run-clang-format/action.yaml: -------------------------------------------------------------------------------- 1 | name: Run clang-format 2 | description: Runs clang-format and checks for any changes introduced by it 3 | inputs: 4 | failCondition: 5 | description: Controls whether failed checks also fail the workflow run 6 | required: false 7 | default: 'never' 8 | workingDirectory: 9 | description: Working directory for checks 10 | required: false 11 | default: ${{ github.workspace }} 12 | runs: 13 | using: composite 14 | steps: 15 | - name: Check Runner Operating System 🏃‍♂️ 16 | if: runner.os == 'Windows' 17 | shell: bash 18 | run: | 19 | : Check Runner Operating System 🏃‍♂️ 20 | echo "::notice::run-clang-format action requires a macOS-based or Linux-based runner." 21 | exit 2 22 | 23 | - name: Install Dependencies 🛍️ 24 | if: runner.os == 'Linux' 25 | shell: bash 26 | run: | 27 | : Install Dependencies 🛍️ 28 | echo ::group::Install Dependencies 29 | eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" 30 | echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH 31 | echo "/home/linuxbrew/.linuxbrew/opt/clang-format@17/bin" >> $GITHUB_PATH 32 | brew install --quiet zsh 33 | echo ::endgroup:: 34 | 35 | - name: Run clang-format 🐉 36 | id: result 37 | shell: zsh --no-rcs --errexit --pipefail {0} 38 | working-directory: ${{ inputs.workingDirectory }} 39 | env: 40 | GITHUB_EVENT_FORCED: ${{ github.event.forced }} 41 | GITHUB_REF_BEFORE: ${{ github.event.before }} 42 | run: | 43 | : Run clang-format 🐉 44 | if (( ${+RUNNER_DEBUG} )) setopt XTRACE 45 | 46 | local -a changes=($(git diff --name-only HEAD~1 HEAD)) 47 | case ${GITHUB_EVENT_NAME} { 48 | pull_request) changes=($(git diff --name-only origin/${GITHUB_BASE_REF} HEAD)) ;; 49 | push) if [[ ${GITHUB_EVENT_FORCED} != true ]] changes=($(git diff --name-only ${GITHUB_REF_BEFORE} HEAD)) ;; 50 | *) ;; 51 | } 52 | 53 | if (( ${changes[(I)(*.c|*.h|*.cpp|*.hpp|*.m|*.mm)]} )) { 54 | echo ::group::Install clang-format-17 55 | brew install --quiet obsproject/tools/clang-format@17 56 | echo ::endgroup:: 57 | 58 | echo ::group::Run clang-format-17 59 | ./build-aux/run-clang-format --fail-${{ inputs.failCondition }} --check 60 | echo ::endgroup:: 61 | } 62 | -------------------------------------------------------------------------------- /.github/actions/run-cmake-format/action.yaml: -------------------------------------------------------------------------------- 1 | name: Run cmake-format 2 | description: Runs cmake-format and checks for any changes introduced by it 3 | inputs: 4 | failCondition: 5 | description: Controls whether failed checks also fail the workflow run 6 | required: false 7 | default: 'never' 8 | workingDirectory: 9 | description: Working directory for checks 10 | required: false 11 | default: ${{ github.workspace }} 12 | runs: 13 | using: composite 14 | steps: 15 | - name: Check Runner Operating System 🏃‍♂️ 16 | if: runner.os == 'Windows' 17 | shell: bash 18 | run: | 19 | : Check Runner Operating System 🏃‍♂️ 20 | echo "::notice::run-cmake-format action requires a macOS-based or Linux-based runner." 21 | exit 2 22 | 23 | - name: Install Dependencies 🛍️ 24 | if: runner.os == 'Linux' 25 | shell: bash 26 | run: | 27 | : Install Dependencies 🛍️ 28 | echo ::group::Install Dependencies 29 | eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" 30 | echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH 31 | brew install --quiet zsh 32 | echo ::endgroup:: 33 | 34 | - name: Run cmake-format 🎛️ 35 | id: result 36 | shell: zsh --no-rcs --errexit --pipefail {0} 37 | working-directory: ${{ github.workspace }} 38 | env: 39 | GITHUB_EVENT_FORCED: ${{ github.event.forced }} 40 | GITHUB_REF_BEFORE: ${{ github.event.before }} 41 | run: | 42 | : Run cmake-format 🎛️ 43 | if (( ${+RUNNER_DEBUG} )) setopt XTRACE 44 | 45 | local -a changes=($(git diff --name-only HEAD~1 HEAD)) 46 | case ${GITHUB_EVENT_NAME} { 47 | pull_request) changes=($(git diff --name-only origin/${GITHUB_BASE_REF} HEAD)) ;; 48 | push) if [[ ${GITHUB_EVENT_FORCED} != true ]] changes=($(git diff --name-only ${GITHUB_REF_BEFORE} HEAD)) ;; 49 | *) ;; 50 | } 51 | 52 | if (( ${changes[(I)*.cmake|*CMakeLists.txt]} )) { 53 | echo ::group::Install cmakelang 54 | pip3 install cmakelang 55 | echo ::endgroup:: 56 | echo ::group::Run cmake-format 57 | ./build-aux/run-cmake-format --fail-${{ inputs.failCondition }} --check 58 | echo ::endgroup:: 59 | } 60 | -------------------------------------------------------------------------------- /.github/scripts/.Aptfile: -------------------------------------------------------------------------------- 1 | package 'cmake' 2 | package 'ccache' 3 | package 'git' 4 | package 'jq' 5 | package 'ninja-build', bin: 'ninja' 6 | package 'pkg-config' 7 | -------------------------------------------------------------------------------- /.github/scripts/.Brewfile: -------------------------------------------------------------------------------- 1 | brew "ccache" 2 | brew "coreutils" 3 | brew "git" 4 | brew "jq" 5 | brew "xcbeautify" 6 | -------------------------------------------------------------------------------- /.github/scripts/.Wingetfile: -------------------------------------------------------------------------------- 1 | package 'cmake', path: 'Cmake\bin', bin: 'cmake' 2 | package 'innosetup', path: 'Inno Setup 6', bin: 'iscc' 3 | -------------------------------------------------------------------------------- /.github/scripts/Build-Windows.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param( 3 | [ValidateSet('x64')] 4 | [string] $Target = 'x64', 5 | [ValidateSet('Debug', 'RelWithDebInfo', 'Release', 'MinSizeRel')] 6 | [string] $Configuration = 'RelWithDebInfo', 7 | [switch] $SkipAll, 8 | [switch] $SkipBuild, 9 | [switch] $SkipDeps 10 | ) 11 | 12 | $ErrorActionPreference = 'Stop' 13 | 14 | if ( $DebugPreference -eq 'Continue' ) { 15 | $VerbosePreference = 'Continue' 16 | $InformationPreference = 'Continue' 17 | } 18 | 19 | if ( ! ( [System.Environment]::Is64BitOperatingSystem ) ) { 20 | throw "A 64-bit system is required to build the project." 21 | } 22 | 23 | if ( $PSVersionTable.PSVersion -lt '7.0.0' ) { 24 | Write-Warning 'The obs-deps PowerShell build script requires PowerShell Core 7. Install or upgrade your PowerShell version: https://aka.ms/pscore6' 25 | exit 2 26 | } 27 | 28 | function Build { 29 | trap { 30 | Pop-Location -Stack BuildTemp -ErrorAction 'SilentlyContinue' 31 | Write-Error $_ 32 | Log-Group 33 | exit 2 34 | } 35 | 36 | $ScriptHome = $PSScriptRoot 37 | $ProjectRoot = Resolve-Path -Path "$PSScriptRoot/../.." 38 | $BuildSpecFile = "${ProjectRoot}/buildspec.json" 39 | 40 | $UtilityFunctions = Get-ChildItem -Path $PSScriptRoot/utils.pwsh/*.ps1 -Recurse 41 | 42 | foreach($Utility in $UtilityFunctions) { 43 | Write-Debug "Loading $($Utility.FullName)" 44 | . $Utility.FullName 45 | } 46 | 47 | $BuildSpec = Get-Content -Path ${BuildSpecFile} -Raw | ConvertFrom-Json 48 | $ProductName = $BuildSpec.name 49 | $ProductVersion = $BuildSpec.version 50 | 51 | if ( ! $SkipDeps ) { 52 | Install-BuildDependencies -WingetFile "${ScriptHome}/.Wingetfile" 53 | } 54 | 55 | Push-Location -Stack BuildTemp 56 | if ( ! ( ( $SkipAll ) -or ( $SkipBuild ) ) ) { 57 | Ensure-Location $ProjectRoot 58 | 59 | $CmakeArgs = @() 60 | $CmakeBuildArgs = @() 61 | $CmakeInstallArgs = @() 62 | 63 | if ( $VerbosePreference -eq 'Continue' ) { 64 | $CmakeBuildArgs += ('--verbose') 65 | $CmakeInstallArgs += ('--verbose') 66 | } 67 | 68 | if ( $DebugPreference -eq 'Continue' ) { 69 | $CmakeArgs += ('--debug-output') 70 | } 71 | 72 | $Preset = "windows-$(if ( $Env:CI -ne $null ) { 'ci-' })${Target}" 73 | 74 | $CmakeArgs += @( 75 | '--preset', $Preset 76 | ) 77 | 78 | $CmakeBuildArgs += @( 79 | '--build' 80 | '--preset', $Preset 81 | '--config', $Configuration 82 | '--parallel' 83 | '--', '/consoleLoggerParameters:Summary', '/noLogo' 84 | ) 85 | 86 | $CmakeInstallArgs += @( 87 | '--install', "build_${Target}" 88 | '--prefix', "${ProjectRoot}/release/${Configuration}" 89 | '--config', $Configuration 90 | ) 91 | 92 | Log-Group "Configuring ${ProductName}..." 93 | Invoke-External cmake @CmakeArgs 94 | 95 | Log-Group "Building ${ProductName}..." 96 | Invoke-External cmake @CmakeBuildArgs 97 | } 98 | Log-Group "Install ${ProductName}..." 99 | Invoke-External cmake @CmakeInstallArgs 100 | 101 | Pop-Location -Stack BuildTemp 102 | Log-Group 103 | } 104 | 105 | Build 106 | -------------------------------------------------------------------------------- /.github/scripts/Package-Windows.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param( 3 | [ValidateSet('x64')] 4 | [string] $Target = 'x64', 5 | [ValidateSet('Debug', 'RelWithDebInfo', 'Release', 'MinSizeRel')] 6 | [string] $Configuration = 'RelWithDebInfo', 7 | [switch] $BuildInstaller, 8 | [switch] $SkipDeps 9 | ) 10 | 11 | $ErrorActionPreference = 'Stop' 12 | 13 | if ( $DebugPreference -eq 'Continue' ) { 14 | $VerbosePreference = 'Continue' 15 | $InformationPreference = 'Continue' 16 | } 17 | 18 | if ( ! ( [System.Environment]::Is64BitOperatingSystem ) ) { 19 | throw "Packaging script requires a 64-bit system to build and run." 20 | } 21 | 22 | 23 | if ( $PSVersionTable.PSVersion -lt '7.0.0' ) { 24 | Write-Warning 'The packaging script requires PowerShell Core 7. Install or upgrade your PowerShell version: https://aka.ms/pscore6' 25 | exit 2 26 | } 27 | 28 | function Package { 29 | trap { 30 | Pop-Location -Stack BuildTemp -ErrorAction 'SilentlyContinue' 31 | Write-Error $_ 32 | Log-Group 33 | exit 2 34 | } 35 | 36 | $ScriptHome = $PSScriptRoot 37 | $ProjectRoot = Resolve-Path -Path "$PSScriptRoot/../.." 38 | $BuildSpecFile = "${ProjectRoot}/buildspec.json" 39 | 40 | $UtilityFunctions = Get-ChildItem -Path $PSScriptRoot/utils.pwsh/*.ps1 -Recurse 41 | 42 | foreach( $Utility in $UtilityFunctions ) { 43 | Write-Debug "Loading $($Utility.FullName)" 44 | . $Utility.FullName 45 | } 46 | 47 | $BuildSpec = Get-Content -Path ${BuildSpecFile} -Raw | ConvertFrom-Json 48 | $ProductName = $BuildSpec.name 49 | $ProductVersion = $BuildSpec.version 50 | 51 | $OutputName = "${ProductName}-${ProductVersion}-windows-${Target}" 52 | 53 | if ( ! $SkipDeps ) { 54 | Install-BuildDependencies -WingetFile "${ScriptHome}/.Wingetfile" 55 | } 56 | 57 | $RemoveArgs = @{ 58 | ErrorAction = 'SilentlyContinue' 59 | Path = @( 60 | "${ProjectRoot}/release/${ProductName}-*-windows-*.zip" 61 | "${ProjectRoot}/release/${ProductName}-*-windows-*.exe" 62 | ) 63 | } 64 | 65 | Remove-Item @RemoveArgs 66 | 67 | Log-Group "Archiving ${ProductName}..." 68 | $CompressArgs = @{ 69 | Path = (Get-ChildItem -Path "${ProjectRoot}/release/${Configuration}" -Exclude "${OutputName}*.*") 70 | CompressionLevel = 'Optimal' 71 | DestinationPath = "${ProjectRoot}/release/${OutputName}.zip" 72 | Verbose = ($Env:CI -ne $null) 73 | } 74 | Compress-Archive -Force @CompressArgs 75 | Log-Group 76 | 77 | if ( ( $BuildInstaller ) ) { 78 | Log-Group "Packaging ${ProductName}..." 79 | 80 | $IsccFile = "${ProjectRoot}/build_${Target}/installer-Windows.generated.iss" 81 | if ( ! ( Test-Path -Path $IsccFile ) ) { 82 | throw 'InnoSetup install script not found. Run the build script or the CMake build and install procedures first.' 83 | } 84 | 85 | Log-Information 'Creating InnoSetup installer...' 86 | Push-Location -Stack BuildTemp 87 | Ensure-Location -Path "${ProjectRoot}/release" 88 | Copy-Item -Path ${Configuration} -Destination Package -Recurse 89 | Invoke-External iscc ${IsccFile} /O"${ProjectRoot}/release" /F"${OutputName}-Installer" 90 | Remove-Item -Path Package -Recurse 91 | Pop-Location -Stack BuildTemp 92 | 93 | Log-Group 94 | } 95 | } 96 | 97 | Package 98 | -------------------------------------------------------------------------------- /.github/scripts/build-linux: -------------------------------------------------------------------------------- 1 | .build.zsh -------------------------------------------------------------------------------- /.github/scripts/build-macos: -------------------------------------------------------------------------------- 1 | .build.zsh -------------------------------------------------------------------------------- /.github/scripts/package-linux: -------------------------------------------------------------------------------- 1 | .package.zsh -------------------------------------------------------------------------------- /.github/scripts/package-macos: -------------------------------------------------------------------------------- 1 | .package.zsh -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Ensure-Location.ps1: -------------------------------------------------------------------------------- 1 | function Ensure-Location { 2 | <# 3 | .SYNOPSIS 4 | Ensures current location to be set to specified directory. 5 | .DESCRIPTION 6 | If specified directory exists, switch to it. Otherwise create it, 7 | then switch. 8 | .EXAMPLE 9 | Ensure-Location "My-Directory" 10 | Ensure-Location -Path "Path-To-My-Directory" 11 | #> 12 | 13 | param( 14 | [Parameter(Mandatory)] 15 | [string] $Path 16 | ) 17 | 18 | if ( ! ( Test-Path $Path ) ) { 19 | $_Params = @{ 20 | ItemType = "Directory" 21 | Path = ${Path} 22 | ErrorAction = "SilentlyContinue" 23 | } 24 | 25 | New-Item @_Params | Set-Location 26 | } else { 27 | Set-Location -Path ${Path} 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Expand-ArchiveExt.ps1: -------------------------------------------------------------------------------- 1 | function Expand-ArchiveExt { 2 | <# 3 | .SYNOPSIS 4 | Expands archive files. 5 | .DESCRIPTION 6 | Allows extraction of zip, 7z, gz, and xz archives. 7 | Requires tar and 7-zip to be available on the system. 8 | Archives ending with .zip but created using LZMA compression are 9 | expanded using 7-zip as a fallback. 10 | .EXAMPLE 11 | Expand-ArchiveExt -Path 12 | Expand-ArchiveExt -Path -DestinationPath 13 | #> 14 | 15 | param( 16 | [Parameter(Mandatory)] 17 | [string] $Path, 18 | [string] $DestinationPath = [System.IO.Path]::GetFileNameWithoutExtension($Path), 19 | [switch] $Force 20 | ) 21 | 22 | switch ( [System.IO.Path]::GetExtension($Path) ) { 23 | .zip { 24 | try { 25 | Expand-Archive -Path $Path -DestinationPath $DestinationPath -Force:$Force 26 | } catch { 27 | if ( Get-Command 7z ) { 28 | Invoke-External 7z x -y $Path "-o${DestinationPath}" 29 | } else { 30 | throw "Fallback utility 7-zip not found. Please install 7-zip first." 31 | } 32 | } 33 | break 34 | } 35 | { ( $_ -eq ".7z" ) -or ( $_ -eq ".exe" ) } { 36 | if ( Get-Command 7z ) { 37 | Invoke-External 7z x -y $Path "-o${DestinationPath}" 38 | } else { 39 | throw "Extraction utility 7-zip not found. Please install 7-zip first." 40 | } 41 | break 42 | } 43 | .gz { 44 | try { 45 | Invoke-External tar -x -o $DestinationPath -f $Path 46 | } catch { 47 | if ( Get-Command 7z ) { 48 | Invoke-External 7z x -y $Path "-o${DestinationPath}" 49 | } else { 50 | throw "Fallback utility 7-zip not found. Please install 7-zip first." 51 | } 52 | } 53 | break 54 | } 55 | .xz { 56 | try { 57 | Invoke-External tar -x -o $DestinationPath -f $Path 58 | } catch { 59 | if ( Get-Command 7z ) { 60 | Invoke-External 7z x -y $Path "-o${DestinationPath}" 61 | } else { 62 | throw "Fallback utility 7-zip not found. Please install 7-zip first." 63 | } 64 | } 65 | } 66 | default { 67 | throw "Unsupported archive extension provided." 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Install-BuildDependencies.ps1: -------------------------------------------------------------------------------- 1 | function Install-BuildDependencies { 2 | <# 3 | .SYNOPSIS 4 | Installs required build dependencies. 5 | .DESCRIPTION 6 | Additional packages might be needed for successful builds. This module contains additional 7 | dependencies available for installation via winget and, if possible, adds their locations 8 | to the environment path for future invocation. 9 | .EXAMPLE 10 | Install-BuildDependencies 11 | #> 12 | 13 | param( 14 | [string] $WingetFile = "$PSScriptRoot/.Wingetfile" 15 | ) 16 | 17 | if ( ! ( Test-Path function:Log-Warning ) ) { 18 | . $PSScriptRoot/Logger.ps1 19 | } 20 | 21 | $Prefixes = @{ 22 | 'x64' = ${Env:ProgramFiles} 23 | 'x86' = ${Env:ProgramFiles(x86)} 24 | 'arm64' = ${Env:ProgramFiles(arm)} 25 | } 26 | 27 | $Paths = $Env:Path -split [System.IO.Path]::PathSeparator 28 | 29 | $WingetOptions = @('install', '--accept-package-agreements', '--accept-source-agreements') 30 | 31 | if ( $script:Quiet ) { 32 | $WingetOptions += '--silent' 33 | } 34 | 35 | Log-Group 'Check Windows build requirements' 36 | Get-Content $WingetFile | ForEach-Object { 37 | $_, $Package, $_, $Path, $_, $Binary, $_, $Version = $_ -replace ',','' -split " +(?=(?:[^\']*\'[^\']*\')*[^\']*$)" -replace "'",'' 38 | 39 | $Prefixes.GetEnumerator() | ForEach-Object { 40 | $Prefix = $_.value 41 | $FullPath = "${Prefix}\${Path}" 42 | if ( ( Test-Path $FullPath ) -and ! ( $Paths -contains $FullPath ) ) { 43 | $Paths = @($FullPath) + $Paths 44 | $Env:Path = $Paths -join [System.IO.Path]::PathSeparator 45 | } 46 | } 47 | 48 | Log-Debug "Checking for command ${Binary}" 49 | $Found = Get-Command -ErrorAction SilentlyContinue $Binary 50 | 51 | if ( $Found ) { 52 | Log-Status "Found dependency ${Binary} as $($Found.Source)" 53 | } else { 54 | Log-Status "Installing package ${Package} $(if ( $Version -ne $null ) { "Version: ${Version}" } )" 55 | 56 | if ( $Version -ne $null ) { 57 | $WingetOptions += @('--version', ${Version}) 58 | } 59 | 60 | try { 61 | $Params = $WingetOptions + $Package 62 | 63 | winget @Params 64 | } catch { 65 | throw "Error while installing winget package ${Package}: $_" 66 | } 67 | } 68 | } 69 | Log-Group 70 | } 71 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Invoke-External.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-External { 2 | <# 3 | .SYNOPSIS 4 | Invokes a non-PowerShell command. 5 | .DESCRIPTION 6 | Runs a non-PowerShell command, and captures its return code. 7 | Throws an exception if the command returns non-zero. 8 | .EXAMPLE 9 | Invoke-External 7z x $MyArchive 10 | #> 11 | 12 | if ( $args.Count -eq 0 ) { 13 | throw 'Invoke-External called without arguments.' 14 | } 15 | 16 | if ( ! ( Test-Path function:Log-Information ) ) { 17 | . $PSScriptRoot/Logger.ps1 18 | } 19 | 20 | $Command = $args[0] 21 | $CommandArgs = @() 22 | 23 | if ( $args.Count -gt 1) { 24 | $CommandArgs = $args[1..($args.Count - 1)] 25 | } 26 | 27 | $_EAP = $ErrorActionPreference 28 | $ErrorActionPreference = "Continue" 29 | 30 | Log-Debug "Invoke-External: ${Command} ${CommandArgs}" 31 | 32 | & $command $commandArgs 33 | $Result = $LASTEXITCODE 34 | 35 | $ErrorActionPreference = $_EAP 36 | 37 | if ( $Result -ne 0 ) { 38 | throw "${Command} ${CommandArgs} exited with non-zero code ${Result}." 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Logger.ps1: -------------------------------------------------------------------------------- 1 | function Log-Debug { 2 | [CmdletBinding()] 3 | param( 4 | [Parameter(Mandatory,ValueFromPipeline)] 5 | [ValidateNotNullOrEmpty()] 6 | [string[]] $Message 7 | ) 8 | 9 | Process { 10 | foreach($m in $Message) { 11 | Write-Debug "$(if ( $env:CI -ne $null ) { '::debug::' })$m" 12 | } 13 | } 14 | } 15 | 16 | function Log-Verbose { 17 | [CmdletBinding()] 18 | param( 19 | [Parameter(Mandatory,ValueFromPipeline)] 20 | [ValidateNotNullOrEmpty()] 21 | [string[]] $Message 22 | ) 23 | 24 | Process { 25 | foreach($m in $Message) { 26 | Write-Verbose $m 27 | } 28 | } 29 | } 30 | 31 | function Log-Warning { 32 | [CmdletBinding()] 33 | param( 34 | [Parameter(Mandatory,ValueFromPipeline)] 35 | [ValidateNotNullOrEmpty()] 36 | [string[]] $Message 37 | ) 38 | 39 | Process { 40 | foreach($m in $Message) { 41 | Write-Warning "$(if ( $env:CI -ne $null ) { '::warning::' })$m" 42 | } 43 | } 44 | } 45 | 46 | function Log-Error { 47 | [CmdletBinding()] 48 | param( 49 | [Parameter(Mandatory,ValueFromPipeline)] 50 | [ValidateNotNullOrEmpty()] 51 | [string[]] $Message 52 | ) 53 | 54 | Process { 55 | foreach($m in $Message) { 56 | Write-Error "$(if ( $env:CI -ne $null ) { '::error::' })$m" 57 | } 58 | } 59 | } 60 | 61 | function Log-Information { 62 | [CmdletBinding()] 63 | param( 64 | [Parameter(Mandatory,ValueFromPipeline)] 65 | [ValidateNotNullOrEmpty()] 66 | [string[]] $Message 67 | ) 68 | 69 | Process { 70 | if ( ! ( $script:Quiet ) ) { 71 | $StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' }) 72 | $Icon = ' =>' 73 | 74 | foreach($m in $Message) { 75 | Write-Host -NoNewLine -ForegroundColor Blue " ${StageName} $($Icon.PadRight(5)) " 76 | Write-Host "${m}" 77 | } 78 | } 79 | } 80 | } 81 | 82 | function Log-Group { 83 | [CmdletBinding()] 84 | param( 85 | [Parameter(ValueFromPipeline)] 86 | [string[]] $Message 87 | ) 88 | 89 | Process { 90 | if ( $Env:CI -ne $null ) { 91 | if ( $script:LogGroup ) { 92 | Write-Output '::endgroup::' 93 | $script:LogGroup = $false 94 | } 95 | 96 | if ( $Message.count -ge 1 ) { 97 | Write-Output "::group::$($Message -join ' ')" 98 | $script:LogGroup = $true 99 | } 100 | } else { 101 | if ( $Message.count -ge 1 ) { 102 | Log-Information $Message 103 | } 104 | } 105 | } 106 | } 107 | 108 | function Log-Status { 109 | [CmdletBinding()] 110 | param( 111 | [Parameter(Mandatory,ValueFromPipeline)] 112 | [ValidateNotNullOrEmpty()] 113 | [string[]] $Message 114 | ) 115 | 116 | Process { 117 | if ( ! ( $script:Quiet ) ) { 118 | $StageName = $( if ( $StageName -ne $null ) { $StageName } else { '' }) 119 | $Icon = ' >' 120 | 121 | foreach($m in $Message) { 122 | Write-Host -NoNewLine -ForegroundColor Green " ${StageName} $($Icon.PadRight(5)) " 123 | Write-Host "${m}" 124 | } 125 | } 126 | } 127 | } 128 | 129 | function Log-Output { 130 | [CmdletBinding()] 131 | param( 132 | [Parameter(Mandatory,ValueFromPipeline)] 133 | [ValidateNotNullOrEmpty()] 134 | [string[]] $Message 135 | ) 136 | 137 | Process { 138 | if ( ! ( $script:Quiet ) ) { 139 | $StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' }) 140 | $Icon = '' 141 | 142 | foreach($m in $Message) { 143 | Write-Output " ${StageName} $($Icon.PadRight(5)) ${m}" 144 | } 145 | } 146 | } 147 | } 148 | 149 | $Columns = (Get-Host).UI.RawUI.WindowSize.Width - 5 150 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/check_linux: -------------------------------------------------------------------------------- 1 | autoload -Uz log_info log_status log_error log_debug log_warning log_group 2 | 3 | log_group 'Check Linux build requirements' 4 | log_debug 'Checking Linux distribution name and version...' 5 | 6 | # Check for Ubuntu version 22.10 or later, which have srt and librist available via apt-get 7 | typeset -g -i UBUNTU_2210_OR_LATER=0 8 | if [[ -f /etc/os_release ]] { 9 | local dist_name 10 | local dist_version 11 | read -r dist_name dist_version <<< "$(source /etc/os_release; print "${NAME} ${VERSION_ID}")" 12 | 13 | autoload -Uz is-at-least 14 | if [[ ${dist_name} == Ubuntu ]] && is-at-least 22.10 ${dist_version}; then 15 | typeset -g -i UBUNTU_2210_OR_LATER=1 16 | fi 17 | } 18 | 19 | log_debug 'Checking for apt-get...' 20 | if (( ! ${+commands[apt-get]} )) { 21 | log_error 'No apt-get command found. Please install apt' 22 | return 2 23 | } else { 24 | log_debug "Apt-get located at ${commands[apt-get]}" 25 | } 26 | 27 | local -a dependencies=("${(fA)$(<${SCRIPT_HOME}/.Aptfile)}") 28 | local -a install_list 29 | local binary 30 | 31 | sudo apt-get update -qq 32 | 33 | for dependency (${dependencies}) { 34 | local -a tokens=(${=dependency//(,|:|\')/}) 35 | 36 | if [[ ! ${tokens[1]} == 'package' ]] continue 37 | 38 | if [[ ${#tokens} -gt 2 && ${tokens[3]} == 'bin' ]] { 39 | binary=${tokens[4]} 40 | } else { 41 | binary=${tokens[2]} 42 | } 43 | 44 | if (( ! ${+commands[${binary}]} )) install_list+=(${tokens[2]}) 45 | } 46 | 47 | log_debug "List of dependencies to install: ${install_list}" 48 | if (( ${#install_list} )) { 49 | if (( ! ${+CI} )) log_warning 'Dependency installation via apt may require elevated privileges' 50 | 51 | local -a apt_args=( 52 | ${CI:+-y} 53 | --no-install-recommends 54 | ) 55 | if (( _loglevel == 0 )) apt_args+=(--quiet) 56 | 57 | sudo apt-get ${apt_args} install ${install_list} 58 | } 59 | 60 | rehash 61 | log_group 62 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/check_macos: -------------------------------------------------------------------------------- 1 | autoload -Uz is-at-least log_group log_info log_error log_status read_codesign 2 | 3 | local macos_version=$(sw_vers -productVersion) 4 | 5 | log_group 'Install macOS build requirements' 6 | log_info 'Checking macOS version...' 7 | if ! is-at-least 11.0 ${macos_version}; then 8 | log_error "Minimum required macOS version is 11.0, but running on macOS ${macos_version}" 9 | return 2 10 | else 11 | log_status "macOS ${macos_version} is recent" 12 | fi 13 | 14 | log_info 'Checking for Homebrew...' 15 | if (( ! ${+commands[brew]} )) { 16 | log_error 'No Homebrew command found. Please install Homebrew (https://brew.sh)' 17 | return 2 18 | } 19 | 20 | brew bundle --file ${SCRIPT_HOME}/.Brewfile 21 | 22 | # Install specific cmake version 23 | local cmake_version=$(cmake --version 2>/dev/null | grep -o '3.31.6') 24 | if [[ ! "${cmake_version}" ]]; then 25 | brew unlink cmake 26 | brew tap-new local/tap 27 | brew extract cmake local/tap --version 3.31.6 28 | brew install local/tap/cmake@3.31.6 29 | fi 30 | 31 | rehash 32 | log_group 33 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_debug: -------------------------------------------------------------------------------- 1 | if (( ! ${+_loglevel} )) typeset -g _loglevel=1 2 | 3 | if (( _loglevel > 2 )) print -PR -e -- "${CI:+::debug::}%F{220}DEBUG: ${@}%f" 4 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_error: -------------------------------------------------------------------------------- 1 | local icon=' ✖︎ ' 2 | 3 | print -u2 -PR "${CI:+::error::}%F{1} ${icon} %f ${@}" 4 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_group: -------------------------------------------------------------------------------- 1 | autoload -Uz log_info 2 | 3 | if (( ! ${+_log_group} )) typeset -g _log_group=0 4 | 5 | if (( ${+CI} )) { 6 | if (( _log_group )) { 7 | print "::endgroup::" 8 | typeset -g _log_group=0 9 | } 10 | if (( # )) { 11 | print "::group::${@}" 12 | typeset -g _log_group=1 13 | } 14 | } else { 15 | if (( # )) log_info ${@} 16 | } 17 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_info: -------------------------------------------------------------------------------- 1 | if (( ! ${+_loglevel} )) typeset -g _loglevel=1 2 | 3 | if (( _loglevel > 0 )) { 4 | local icon=' =>' 5 | 6 | print -PR "%F{4} ${(r:5:)icon}%f %B${@}%b" 7 | } 8 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_output: -------------------------------------------------------------------------------- 1 | if (( ! ${+_loglevel} )) typeset -g _loglevel=1 2 | 3 | if (( _loglevel > 0 )) { 4 | local icon='' 5 | 6 | print -PR " ${(r:5:)icon} ${@}" 7 | } 8 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_status: -------------------------------------------------------------------------------- 1 | if (( ! ${+_loglevel} )) typeset -g _loglevel=1 2 | 3 | if (( _loglevel > 0 )) { 4 | local icon=' >' 5 | 6 | print -PR "%F{2} ${(r:5:)icon}%f ${@}" 7 | } 8 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_warning: -------------------------------------------------------------------------------- 1 | if (( _loglevel > 0 )) { 2 | local icon=' =>' 3 | 4 | print -PR "${CI:+::warning::}%F{3} ${(r:5:)icon} ${@}%f" 5 | } 6 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/mkcd: -------------------------------------------------------------------------------- 1 | [[ -n ${1} ]] && mkdir -p ${1} && builtin cd ${1} 2 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/read_codesign: -------------------------------------------------------------------------------- 1 | autoload -Uz log_info 2 | 3 | if (( ! ${+CODESIGN_IDENT} )) { 4 | typeset -g CODESIGN_IDENT 5 | log_info 'Setting up Apple Developer ID for application codesigning...' 6 | read CODESIGN_IDENT'?Apple Developer Application ID: ' 7 | } 8 | 9 | typeset -g CODESIGN_TEAM=$(print "${CODESIGN_IDENT}" | /usr/bin/sed -En 's/.+\((.+)\)/\1/p') 10 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/read_codesign_installer: -------------------------------------------------------------------------------- 1 | autoload -Uz log_info 2 | 3 | if (( ! ${+CODESIGN_IDENT_INSTALLER} )) { 4 | typeset -g CODESIGN_IDENT_INSTALLER 5 | log_info 'Setting up Apple Developer Installer ID for installer package codesigning...' 6 | read CODESIGN_IDENT_INSTALLER'?Apple Developer Installer ID: ' 7 | } 8 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/read_codesign_pass: -------------------------------------------------------------------------------- 1 | ############################################################################## 2 | # Apple Developer credentials necessary: 3 | # 4 | # + Signing for distribution and notarization require an active Apple 5 | # Developer membership 6 | # + An Apple Development identity is needed for code signing 7 | # (i.e. 'Apple Development: YOUR APPLE ID (PROVIDER)') 8 | # + Your Apple developer ID is needed for notarization 9 | # + An app-specific password is necessary for notarization from CLI 10 | # + This password will be stored in your macOS keychain under the identifier 11 | # 'OBS-Codesign-Password'with access Apple's 'altool' only. 12 | ############################################################################## 13 | 14 | autoload -Uz read_codesign read_codesign_user log_info log_warning 15 | 16 | if (( ! ${+CODESIGN_IDENT} )) { 17 | read_codesign 18 | } 19 | 20 | if (( ! ${+CODESIGN_IDENT_USER} )) { 21 | read_codesign_user 22 | } 23 | 24 | log_info 'Setting up password for notarization keychain...' 25 | if (( ! ${+CODESIGN_IDENT_PASS} )) { 26 | read -s CODESIGN_IDENT_PASS'?Apple Developer ID password: ' 27 | } 28 | 29 | print '' 30 | log_info 'Setting up notarization keychain...' 31 | log_warning " 32 | + Your Apple ID and an app-specific password is necessary for notarization from CLI 33 | + This password will be stored in your macOS keychain under the identifier 34 | 'OBS-Codesign-Password' with access Apple's 'altool' only. 35 | 36 | " 37 | xcrun notarytool store-credentials 'OBS-Codesign-Password' --apple-id "${CODESIGN_IDENT_USER}" --team-id "${CODESIGN_TEAM}" --password "${CODESIGN_IDENT_PASS}" 38 | 39 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/read_codesign_team: -------------------------------------------------------------------------------- 1 | autoload -Uz log_info 2 | 3 | if (( ! ${+CODESIGN_TEAM} )) { 4 | typeset -g CODESIGN_TEAM 5 | log_info 'Setting up Apple Developer Team ID for codesigning...' 6 | read CODESIGN_TEAM'?Apple Developer Team ID (leave empty to use Apple Developer ID instead): ' 7 | } 8 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/read_codesign_user: -------------------------------------------------------------------------------- 1 | autoload -Uz log_info 2 | 3 | if (( ! ${+CODESIGN_IDENT_USER} )) { 4 | typeset -g CODESIGN_IDENT_USER 5 | log_info 'Setting up Apple ID for notarization...' 6 | read CODESIGN_IDENT_USER'?Apple ID: ' 7 | } 8 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/set_loglevel: -------------------------------------------------------------------------------- 1 | autoload -Uz log_debug log_error 2 | 3 | local -r _usage="Usage: %B${0}%b 4 | 5 | Set log level, following levels are supported: 0 (quiet), 1 (normal), 2 (verbose), 3 (debug)" 6 | 7 | if (( ! # )); then 8 | log_error 'Called without arguments.' 9 | log_output ${_usage} 10 | return 2 11 | elif (( ${1} >= 4 )); then 12 | log_error 'Called with loglevel > 3.' 13 | log_output ${_usage} 14 | fi 15 | 16 | typeset -g -i -r _loglevel=${1} 17 | log_debug "Log level set to '${1}'" 18 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/setup_ccache: -------------------------------------------------------------------------------- 1 | autoload -Uz log_debug log_warning 2 | 3 | if (( ! ${+project_root} )) { 4 | log_error "'project_root' not set. Please set before running ${0}." 5 | return 2 6 | } 7 | 8 | if (( ${+commands[ccache]} )) { 9 | log_debug "Found ccache at ${commands[ccache]}" 10 | 11 | typeset -gx CCACHE_CONFIGPATH="${project_root}/.ccache.conf" 12 | 13 | #ccache --set-config=run_second_cpp=true 14 | ccache --set-config=direct_mode=true 15 | ccache --set-config=inode_cache=true 16 | ccache --set-config=compiler_check=content 17 | ccache --set-config=file_clone=true 18 | 19 | local -a sloppiness=( 20 | include_file_mtime 21 | include_file_ctime 22 | file_stat_matches 23 | system_headers 24 | ) 25 | 26 | if [[ ${host_os} == macos ]] { 27 | sloppiness+=( 28 | modules 29 | clang_index_store 30 | ) 31 | 32 | ccache --set-config=sloppiness=${(j:,:)sloppiness} 33 | } 34 | 35 | if (( ${+CI} )) { 36 | ccache --set-config=cache_dir="${GITHUB_WORKSPACE:-${HOME}}/.ccache" 37 | ccache --set-config=max_size="${CCACHE_SIZE:-1G}" 38 | ccache -z > /dev/null 39 | } 40 | } else { 41 | log_warning "No ccache found on the system" 42 | } 43 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/setup_linux: -------------------------------------------------------------------------------- 1 | autoload -Uz log_error log_status log_info mkcd 2 | 3 | if (( ! ${+project_root} )) { 4 | log_error "'project_root' not set. Please set before running ${0}." 5 | return 2 6 | } 7 | 8 | if (( ! ${+target} )) { 9 | log_error "'target' not set. Please set before running ${0}." 10 | return 2 11 | } 12 | 13 | pushd ${project_root} 14 | 15 | typeset -g QT_VERSION 16 | 17 | local -a apt_args=( 18 | ${CI:+-y} 19 | --no-install-recommends 20 | ) 21 | if (( _loglevel == 0 )) apt_args+=(--quiet) 22 | 23 | if (( ! (${skips[(Ie)all]} + ${skips[(Ie)deps]}) )) { 24 | log_group 'Installing obs-studio build dependencies...' 25 | 26 | local suffix 27 | if [[ ${CPUTYPE} != "${target##*-}" ]] { 28 | local -A arch_mappings=( 29 | aarch64 arm64 30 | x86_64 amd64 31 | ) 32 | 33 | suffix=":${arch_mappings[${target##*-}]}" 34 | 35 | sudo apt-get install ${apt_args} gcc-${${target##*-}//_/-}-linux-gnu g++-${${target##*-}//_/-}-linux-gnu 36 | } 37 | 38 | sudo add-apt-repository --yes ppa:obsproject/obs-studio 39 | sudo apt update 40 | 41 | sudo apt-get install ${apt_args} \ 42 | build-essential \ 43 | libgles2-mesa-dev \ 44 | obs-studio 45 | 46 | local -a _qt_packages=() 47 | 48 | if (( QT_VERSION == 5 )) { 49 | _qt_packages+=( 50 | qtbase5-dev${suffix} 51 | libqt5svg5-dev${suffix} 52 | qtbase5-private-dev${suffix} 53 | libqt5x11extras5-dev${suffix} 54 | ) 55 | } else { 56 | _qt_packages+=( 57 | qt6-base-dev${suffix} 58 | libqt6svg6-dev${suffix} 59 | qt6-base-private-dev${suffix} 60 | ) 61 | } 62 | 63 | sudo apt-get install ${apt_args} ${_qt_packages} 64 | log_group 65 | } 66 | -------------------------------------------------------------------------------- /.github/workflows/check-format.yaml: -------------------------------------------------------------------------------- 1 | name: Check Code Formatting 🛠️ 2 | on: 3 | workflow_call: 4 | jobs: 5 | clang-format: 6 | runs-on: ubuntu-24.04 7 | steps: 8 | - uses: actions/checkout@v4 9 | with: 10 | fetch-depth: 0 11 | - name: clang-format check 🐉 12 | id: clang-format 13 | uses: ./.github/actions/run-clang-format 14 | with: 15 | failCondition: error 16 | 17 | cmake-format: 18 | runs-on: ubuntu-22.04 19 | steps: 20 | - uses: actions/checkout@v4 21 | with: 22 | fetch-depth: 0 23 | - name: cmake-format check 🎛️ 24 | id: cmake-format 25 | uses: ./.github/actions/run-cmake-format 26 | with: 27 | failCondition: error 28 | -------------------------------------------------------------------------------- /.github/workflows/dispatch.yaml: -------------------------------------------------------------------------------- 1 | name: Dispatch 2 | run-name: Dispatched Repository Actions - ${{ inputs.job }} ⌛️ 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | job: 7 | description: Dispatch job to run 8 | required: true 9 | type: choice 10 | options: 11 | - build 12 | permissions: 13 | contents: write 14 | jobs: 15 | check-and-build: 16 | if: inputs.job == 'build' 17 | uses: ./.github/workflows/build-project.yaml 18 | secrets: inherit 19 | -------------------------------------------------------------------------------- /.github/workflows/pr-pull.yaml: -------------------------------------------------------------------------------- 1 | name: Pull Request 2 | run-name: ${{ github.event.pull_request.title }} pull request run 🚀 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | paths-ignore: 7 | - '**.md' 8 | branches: [master, main] 9 | types: [ opened, synchronize, reopened ] 10 | permissions: 11 | contents: read 12 | concurrency: 13 | group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' 14 | cancel-in-progress: true 15 | jobs: 16 | check-format: 17 | name: Check Formatting 🔍 18 | uses: ./.github/workflows/check-format.yaml 19 | permissions: 20 | contents: read 21 | 22 | build-project: 23 | name: Build Project 🧱 24 | uses: ./.github/workflows/build-project.yaml 25 | secrets: inherit 26 | permissions: 27 | contents: read 28 | -------------------------------------------------------------------------------- /.github/workflows/push.yaml: -------------------------------------------------------------------------------- 1 | name: Push to master 2 | run-name: ${{ github.ref_name }} push run 🚀 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - main 8 | - 'release/**' 9 | tags: 10 | - '*' 11 | permissions: 12 | contents: write 13 | concurrency: 14 | group: '${{ github.workflow }} @ ${{ github.ref }}' 15 | cancel-in-progress: ${{ github.ref_type == 'tag' }} 16 | jobs: 17 | check-format: 18 | name: Check Formatting 🔍 19 | if: github.ref_name == 'master' 20 | uses: ./.github/workflows/check-format.yaml 21 | permissions: 22 | contents: read 23 | 24 | build-project: 25 | name: Build Project 🧱 26 | uses: ./.github/workflows/build-project.yaml 27 | secrets: inherit 28 | permissions: 29 | contents: read 30 | 31 | create-release: 32 | name: Create Release 🛫 33 | if: github.ref_type == 'tag' 34 | runs-on: ubuntu-22.04 35 | needs: build-project 36 | defaults: 37 | run: 38 | shell: bash 39 | steps: 40 | - name: Check Release Tag ☑️ 41 | id: check 42 | run: | 43 | : Check Release Tag ☑️ 44 | if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi 45 | shopt -s extglob 46 | 47 | case "${GITHUB_REF_NAME}" in 48 | +([0-9]).+([0-9]).+([0-9]) ) 49 | echo 'validTag=true' >> $GITHUB_OUTPUT 50 | echo 'prerelease=false' >> $GITHUB_OUTPUT 51 | echo "version=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT 52 | ;; 53 | +([0-9]).+([0-9]).+([0-9])-@(beta|rc)*([0-9]) ) 54 | echo 'validTag=true' >> $GITHUB_OUTPUT 55 | echo 'prerelease=true' >> $GITHUB_OUTPUT 56 | echo "version=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT 57 | ;; 58 | *) echo 'validTag=false' >> $GITHUB_OUTPUT ;; 59 | esac 60 | 61 | - name: Download Build Artifacts 📥 62 | uses: actions/download-artifact@v4 63 | if: fromJSON(steps.check.outputs.validTag) 64 | id: download 65 | 66 | - name: Rename Files 🏷️ 67 | if: fromJSON(steps.check.outputs.validTag) 68 | run: | 69 | : Rename Files 🏷️ 70 | if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi 71 | shopt -s extglob 72 | shopt -s nullglob 73 | 74 | root_dir="$(pwd)" 75 | commit_hash="${GITHUB_SHA:0:9}" 76 | 77 | variants=( 78 | 'windows-x64;zip|exe' 79 | 'macos-universal;tar.xz|pkg' 80 | 'ubuntu-22.04-x86_64;tar.xz|deb|ddeb' 81 | 'sources;tar.xz' 82 | ) 83 | 84 | for variant_data in "${variants[@]}"; do 85 | IFS=';' read -r variant suffix <<< "${variant_data}" 86 | 87 | candidates=(*-${variant}-${commit_hash}/@(*|*-dbgsym).@(${suffix})) 88 | 89 | for candidate in "${candidates[@]}"; do 90 | mv "${candidate}" "${root_dir}" 91 | done 92 | done 93 | 94 | - name: Generate Checksums 🪪 95 | if: fromJSON(steps.check.outputs.validTag) 96 | run: | 97 | : Generate Checksums 🪪 98 | if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi 99 | shopt -s extglob 100 | 101 | echo "### Checksums" > ${{ github.workspace }}/CHECKSUMS.txt 102 | for file in ${{ github.workspace }}/@(*.exe|*.deb|*.ddeb|*.pkg|*.tar.xz|*.zip); do 103 | echo " ${file##*/}: $(sha256sum "${file}" | cut -d " " -f 1)" >> ${{ github.workspace }}/CHECKSUMS.txt 104 | done 105 | 106 | - name: Create Release 🛫 107 | if: fromJSON(steps.check.outputs.validTag) 108 | id: create_release 109 | uses: softprops/action-gh-release@9d7c94cfd0a1f3ed45544c887983e9fa900f0564 110 | with: 111 | draft: true 112 | prerelease: ${{ fromJSON(steps.check.outputs.prerelease) }} 113 | tag_name: ${{ steps.check.outputs.version }} 114 | name: ${{ needs.build-project.outputs.pluginName }} ${{ steps.check.outputs.version }} 115 | body_path: ${{ github.workspace }}/CHECKSUMS.txt 116 | files: | 117 | ${{ github.workspace }}/*.exe 118 | ${{ github.workspace }}/*.zip 119 | ${{ github.workspace }}/*.pkg 120 | ${{ github.workspace }}/*.deb 121 | ${{ github.workspace }}/*.ddeb 122 | ${{ github.workspace }}/*.tar.xz 123 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Exclude everything 2 | /* 3 | 4 | # Except for default project files 5 | !/.github 6 | !/build-aux 7 | !/cmake 8 | !/data 9 | !/src 10 | !.clang-format 11 | !.cmake-format.json 12 | !.gitignore 13 | !buildspec.json 14 | !CMakeLists.txt 15 | !CMakePresets.json 16 | !LICENSE 17 | !README.md 18 | 19 | # Exclude lock files 20 | *.lock.json 21 | 22 | # Exclude macOS legacy resource forks 23 | .DS_Store 24 | 25 | # Exclude CMake build number cache 26 | /cmake/.CMakeBuildNumber 27 | 28 | # Except additional project files 29 | !screenshot1.jpg 30 | !screenshot2.jpg 31 | !build-windows-installer.ps1 32 | !README_ja.md 33 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16...3.26) 2 | 3 | include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/bootstrap.cmake" NO_POLICY_SCOPE) 4 | 5 | project(${_name} VERSION ${_version}) 6 | 7 | option(ENABLE_FRONTEND_API "Use obs-frontend-api for UI functionality" ON) 8 | option(ENABLE_QT "Use Qt functionality" ON) 9 | 10 | include(compilerconfig) 11 | include(defaults) 12 | include(helpers) 13 | 14 | add_library(${CMAKE_PROJECT_NAME} MODULE) 15 | 16 | find_package(libobs REQUIRED) 17 | target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE OBS::libobs) 18 | 19 | if(ENABLE_FRONTEND_API) 20 | find_package(obs-frontend-api REQUIRED) 21 | target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE OBS::obs-frontend-api) 22 | endif() 23 | 24 | if(ENABLE_QT) 25 | find_package(Qt6 COMPONENTS Widgets Core) 26 | target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE Qt6::Core Qt6::Widgets) 27 | target_compile_options( 28 | ${CMAKE_PROJECT_NAME} PRIVATE $<$:-Wno-quoted-include-in-framework-header 29 | -Wno-comma>) 30 | set_target_properties( 31 | ${CMAKE_PROJECT_NAME} 32 | PROPERTIES AUTOMOC ON 33 | AUTOUIC ON 34 | AUTORCC ON) 35 | endif() 36 | 37 | target_sources( 38 | ${CMAKE_PROJECT_NAME} PRIVATE src/plugin-main.cpp src/plugin-ui.cpp src/utils.cpp src/audio/audio-capture.cpp 39 | src/UI/output-status-dock.cpp src/UI/resources.qrc) 40 | 41 | set_target_properties_plugin(${CMAKE_PROJECT_NAME} PROPERTIES OUTPUT_NAME ${_name}) 42 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 22, 6 | "patch": 0 7 | }, 8 | "configurePresets": [ 9 | { 10 | "name": "template", 11 | "hidden": true, 12 | "cacheVariables": { 13 | "ENABLE_FRONTEND_API": true, 14 | "ENABLE_QT": true 15 | } 16 | }, 17 | { 18 | "name": "macos", 19 | "displayName": "macOS Universal", 20 | "description": "Build for macOS 11.0+ (Universal binary)", 21 | "inherits": ["template"], 22 | "binaryDir": "${sourceDir}/build_macos", 23 | "condition": { 24 | "type": "equals", 25 | "lhs": "${hostSystemName}", 26 | "rhs": "Darwin" 27 | }, 28 | "generator": "Xcode", 29 | "warnings": {"dev": true, "deprecated": true}, 30 | "cacheVariables": { 31 | "CMAKE_OSX_DEPLOYMENT_TARGET": "11.0", 32 | "CODESIGN_IDENTITY": "$penv{CODESIGN_IDENT}", 33 | "CODESIGN_TEAM": "$penv{CODESIGN_TEAM}" 34 | } 35 | }, 36 | { 37 | "name": "macos-ci", 38 | "inherits": ["macos"], 39 | "displayName": "macOS Universal CI build", 40 | "description": "Build for macOS 11.0+ (Universal binary) for CI", 41 | "generator": "Xcode", 42 | "cacheVariables": { 43 | "CMAKE_COMPILE_WARNING_AS_ERROR": true 44 | } 45 | }, 46 | { 47 | "name": "windows-x64", 48 | "displayName": "Windows x64", 49 | "description": "Build for Windows x64", 50 | "inherits": ["template"], 51 | "binaryDir": "${sourceDir}/build_x64", 52 | "condition": { 53 | "type": "equals", 54 | "lhs": "${hostSystemName}", 55 | "rhs": "Windows" 56 | }, 57 | "generator": "Visual Studio 17 2022", 58 | "architecture": "x64", 59 | "warnings": {"dev": true, "deprecated": true}, 60 | "cacheVariables": { 61 | "CMAKE_SYSTEM_VERSION": "10.0.18363.657" 62 | } 63 | }, 64 | { 65 | "name": "windows-ci-x64", 66 | "inherits": ["windows-x64"], 67 | "displayName": "Windows x64 CI build", 68 | "description": "Build for Windows x64 on CI", 69 | "cacheVariables": { 70 | "CMAKE_COMPILE_WARNING_AS_ERROR": true 71 | } 72 | }, 73 | { 74 | "name": "linux-x86_64", 75 | "displayName": "Linux x86_64", 76 | "description": "Build for Linux x86_64", 77 | "inherits": ["template"], 78 | "binaryDir": "${sourceDir}/build_x86_64", 79 | "condition": { 80 | "type": "equals", 81 | "lhs": "${hostSystemName}", 82 | "rhs": "Linux" 83 | }, 84 | "generator": "Ninja", 85 | "warnings": {"dev": true, "deprecated": true}, 86 | "cacheVariables": { 87 | "CMAKE_BUILD_TYPE": "RelWithDebInfo" 88 | } 89 | }, 90 | { 91 | "name": "linux-ci-x86_64", 92 | "inherits": ["linux-x86_64"], 93 | "displayName": "Linux x86_64 CI build", 94 | "description": "Build for Linux x86_64 on CI", 95 | "cacheVariables": { 96 | "CMAKE_BUILD_TYPE": "RelWithDebInfo", 97 | "CMAKE_COMPILE_WARNING_AS_ERROR": true 98 | } 99 | }, 100 | { 101 | "name": "linux-aarch64", 102 | "displayName": "Linux aarch64", 103 | "description": "Build for Linux aarch64", 104 | "inherits": ["template"], 105 | "binaryDir": "${sourceDir}/build_aarch64", 106 | "condition": { 107 | "type": "equals", 108 | "lhs": "${hostSystemName}", 109 | "rhs": "Linux" 110 | }, 111 | "generator": "Ninja", 112 | "warnings": {"dev": true, "deprecated": true}, 113 | "cacheVariables": { 114 | "CMAKE_BUILD_TYPE": "RelWithDebInfo" 115 | } 116 | }, 117 | { 118 | "name": "linux-ci-aarch64", 119 | "inherits": ["linux-aarch64"], 120 | "displayName": "Linux aarch64 CI build", 121 | "description": "Build for Linux aarch64 on CI", 122 | "cacheVariables": { 123 | "CMAKE_BUILD_TYPE": "RelWithDebInfo", 124 | "CMAKE_COMPILE_WARNING_AS_ERROR": true 125 | } 126 | } 127 | ], 128 | "buildPresets": [ 129 | { 130 | "name": "macos", 131 | "configurePreset": "macos", 132 | "displayName": "macOS Universal", 133 | "description": "macOS build for Universal architectures", 134 | "configuration": "Release" 135 | }, 136 | { 137 | "name": "macos-ci", 138 | "configurePreset": "macos-ci", 139 | "displayName": "macOS Universal CI", 140 | "description": "macOS CI build for Universal architectures", 141 | "configuration": "RelWithDebInfo" 142 | }, 143 | { 144 | "name": "windows-x64", 145 | "configurePreset": "windows-x64", 146 | "displayName": "Windows x64", 147 | "description": "Windows build for x64", 148 | "configuration": "RelWithDebInfo" 149 | }, 150 | { 151 | "name": "windows-ci-x64", 152 | "configurePreset": "windows-ci-x64", 153 | "displayName": "Windows x64 CI", 154 | "description": "Windows CI build for x64 (RelWithDebInfo configuration)", 155 | "configuration": "RelWithDebInfo" 156 | }, 157 | { 158 | "name": "linux-x86_64", 159 | "configurePreset": "linux-x86_64", 160 | "displayName": "Linux x86_64", 161 | "description": "Linux build for x86_64", 162 | "configuration": "RelWithDebInfo" 163 | }, 164 | { 165 | "name": "linux-ci-x86_64", 166 | "configurePreset": "linux-ci-x86_64", 167 | "displayName": "Linux x86_64 CI", 168 | "description": "Linux CI build for x86_64", 169 | "configuration": "RelWithDebInfo" 170 | }, 171 | { 172 | "name": "linux-aarch64", 173 | "configurePreset": "linux-aarch64", 174 | "displayName": "Linux aarch64", 175 | "description": "Linux build for aarch64", 176 | "configuration": "RelWithDebInfo" 177 | }, 178 | { 179 | "name": "linux-ci-aarch64", 180 | "configurePreset": "linux-ci-aarch64", 181 | "displayName": "Linux aarch64 CI", 182 | "description": "Linux CI build for aarch64", 183 | "configuration": "RelWithDebInfo" 184 | } 185 | ] 186 | } 187 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Branch Output filter (The OBS Studio Plugin) 2 | 3 | [日本語はこちら](./README_ja.md) 4 | 5 | > ## Sponsor program 6 | > 7 | > If you would like to help our projects, please support us. 8 | > 9 | > [](https://github.com/sponsors/OPENSPHERE-Inc) 10 | 11 | [](./screenshot1.jpg) 12 | 13 | [](./screenshot2.jpg) 14 | 15 | ## Features 16 | 17 | This is an OBS Studio plugin that allows to live stream and/or recording for each source individually. 18 | Inspired by the [Source Record](https://github.com/exeldro/obs-source-record) plugin, but more focused on streaming. 19 | More reliable and proper audio handling. 20 | 21 | - Added “Branch Output” to source or scene effect filters. 22 | - One stream per Branch Output filter can be sent with dedicated encoding settings. 23 | - Multiple Branch Outputs can be added to a single source or scene (as PC specs allow) 24 | - Branch Output Selectable audio source for each filter (filter audio, any source audio, audio tracks 1-6) 25 | - Automatically reconnects when disconnected 26 | - Stream recording functionality (Various container formats, time and size division supported) 27 | 28 | (*) Works as recording only if connection information is left blank 29 | 30 | - Status dock to check the status and statistics of all Branch Output filters. Support for batch or individual activation and deactivation 31 | - Can be interlinked with OBS Studio's streaming, recording, and virtual camera status 32 | - Manual splitting of recorded files (Since 1.0.5) 33 | - Pause/Unpause Recording (Since 1.0.5) 34 | - Adding chapter markers to recordings(Only on Hybrid MP4) (Since 1.0.5) 35 | - Various hotkeys 36 | - Enable/Disable the filter 37 | - Manual recording splitting, Pause/Unpause, Adding chapters (Since 1.0.5) 38 | - Enable/Disable all 39 | - Split all recordings, Pause/Unpause all recordings, Add chapter to all reacordings (Since 1.0.5) 40 | - Also available in Studio Mode 41 | - **Branch Output ignore studio mode's program out and always outputs preview's one** 42 | 43 | ## Requirements 44 | 45 | [OBS Studio](https://obsproject.com/) >= 30.1.0 (Qt6, x64/ARM64/AppleSilicon) 46 | 47 | # Installation 48 | 49 | Please download latest install package from [Release](https://github.com/OPENSPHERE-Inc/branch-output/releases) 50 | 51 | # User manual 52 | 53 | 1. Add "Branch Output" as effect filters to any "Source" or "Scene" (NOTE: "Scene" has no audio defaultly) 54 | 2. Input server URL and stream key. The server URL can be RTMP or SRT etc. like OBS's custom stream settings. 55 | 3. Choose audio source. Un-checked custom audio source means use filter audio as source (NOTE: "Scene" 56 | must has custom audio source for it's sound) 57 | 58 | "Any Sources" will be captured after filter pipeline before Audio Mixer. Also "Audio track 1 ~ 6" will be captured from Audio Mixer output. 59 | 60 | You can choose "No Audio" as well. 61 | 62 | 4. Setup audio and video encoder. It's usable that hardware encoder such as NVENC. 63 | 5. Press Apply button and stream will be online. 64 | 6. When filter is inactivated via "Eye icon", output stream will be offline too. 65 | 66 | (*) Some sources (e.g. Local Media source) will stop stream output during inactivated scene. It's not plugin's bug. 67 | 68 | # TIPS 69 | 70 | ## 1. To change the resolution and layout for streaming (Work-around) 71 | 72 | > Changing Resolution feature is integrated since 1.0.0 73 | > 74 | > Below steps are alternative method with more flexibilities (Cropping, Zooming, Adding black bg etc...) 75 | 76 | 1. The canvas resolution is assumed to be 1080p. 77 | 2. Create new blank scene. 78 | 3. Right-click on the source you want to stream and click “Copy” from the menu. 79 | 4. Right-click on the blank scene created in step 2 and click “Paste (Reference)” from the menu. 80 | Scale and crop as needed. 81 | 5. Add Branch Output to the scene's effects filter and set up streaming 82 | 83 | “Paste (Reference)” does not duplicate the source and will not cause device conflicts. 84 | 85 | Branch Output will stream even if the scene is not active. 86 | 87 | This method should work well except for some sources (e.g., media sources) that will not play unless the scene is active. 88 | 89 | ## 2. To stream program out to multiple streaming platforms. 90 | 91 | > **Important note**: Work in studio mode to prevent video loops. 92 | 93 | This can be done by using the plugin [Main View Source](https://obsproject.com/forum/resources/main-view-source.1501/) along with it. 94 | 95 | 1. Create new blank scene (The scene for streaming) 96 | 2. Add a “Main View Source” to the scene for streaming. 97 | 3. Add Branch Output to the scene's effects filter and set up streaming. 98 | 99 | You can also create multiple scenes for streaming and layer additional sources to change the content for each streaming platform. 100 | 101 | For example, you might want to display Twitch comments on Twitch and YouTube comments on YouTube. 102 | 103 | However, this method adds sources but does not reduce them. 104 | 105 | # Development 106 | 107 | This plugin is developed under [obs-plugintemplate](https://github.com/obsproject/obs-plugintemplate) 108 | 109 | -------------------------------------------------------------------------------- /README_ja.md: -------------------------------------------------------------------------------- 1 | # Branch Output filter (The OBS Studio Plugin) 2 | 3 | > ## スポンサープログラム 4 | > 5 | > プロジェクトにご協力いただける方は、是非ご支援ください。 6 | > 7 | > [](https://github.com/sponsors/OPENSPHERE-Inc) 8 | 9 | 10 | [](./screenshot1.jpg) 11 | 12 | [](./screenshot2.jpg) 13 | 14 | ## Features 15 | 16 | この OBS Studio プラグインでは、ソース毎に配信ないし録画するエフェクトフィルタを追加します。 17 | [Source Record](https://github.com/exeldro/obs-source-record) プラグインに触発されて開発しましたが、ストリーミングでの使用に重点が置かれています。 18 | より信頼性があり、適切なオーディオの取り扱いを行います。 19 | 20 | - ソースまたはシーンのエフェクトフィルタに「Branch Output」を追加 21 | - フィルター1つにつき1本のストリーム送出が、専用のエンコーディング設定で可能 22 | - 1つのソース・シーンに複数の Branch Output を追加可能(PCのスペックが許す限り追加可能) 23 | - Branch Output フィルターごとに音声ソースを選択可能(フィルター音声、任意ソース音声、音声トラック1~6) 24 | - 接続が切れても自動的に再接続 25 | - 配信録画機能(各種コンテナ形式、時間・サイズ分割に対応) 26 | 27 | ※接続情報を空欄にすれば録画のみとして動作 28 | 29 | - ステータスドックで全 Branch Output フィルターの状態と統計を確認可能。一括ないし個別の有効化・無効化に対応。 30 | - OBS Studio の配信・録画・仮想カメラの状態と連動可能 31 | - 録画ファイルの手動分割 (1.0.5より) 32 | - 録画のポーズ・アンポーズ (1.0.5より) 33 | - 録画へのチャプチャーマーカー追加(Hybrid MP4のみ対応) (1.0.5より) 34 | - 各種ホットキー 35 | - フィルターEnable/Disable 36 | - 手動録画分割、ポーズ・アンポーズ、チャプターの追加 (1.0.5より) 37 | - 全体Enable/Disable 38 | - 全体録画分割、全体ポーズ・アンポーズ、全体チャプター追加 (1.0.5より) 39 | - スタジオモード対応 40 | - **Branch Output はスタジオモードのプログラム出力を無視し、常にプレビューを出力に使用します** 41 | 42 | ## Requirements 43 | 44 | [OBS Studio](https://obsproject.com/) >= 30.1.0 (Qt6, x64/ARM64/AppleSilicon) 45 | 46 | # Installation 47 | 48 | Please download latest install package from [Release](https://github.com/OPENSPHERE-Inc/branch-output/releases) 49 | 50 | # User manual 51 | 52 | [こちらのブログ記事](https://blog.opensphere.co.jp/posts/branchoutput001) に日本語でより詳しい使い方を掲載していますので参照ください。 53 | 54 | 1. 任意の「ソース」または「シーン」に、エフェクトフィルタとして "Branch Output" を追加 55 | (注意:「シーン」はデフォルトでオーディオがありません) 56 | 2. サーバーURLとストリームキーを入力。 57 | サーバーURLは OBS のカスタム配信設定の様に RTMP や SRT 等を使用できます。 58 | 3. オーディオソースを選択。 59 | カスタムオーディオソースからチェックを外した場合、フィルターオーディオを使用します 60 | (注意:「シーン」の音声は必ずカスタムオーディオソースを使用しなければなりません) 61 | 62 | 「任意のソース」はフィルターパイプラインの後、オーディオミキサーの前で取り込まれます。 63 | 「音声トラック1~6」はオーディオミキサーの出力が取り込まれます。 64 | 65 | 「無音」も選択可能です。 66 | 4. 音声および映像エンコーダーを設定。NVENC 等のハードウェアエンコーダーも使用可能です。 67 | 5. 「適用」ボタンをクリックすると、送信が開始されます。 68 | 6. 「目」アイコンでフィルターが非アクティブ化されると、出力ストリームもオフラインになります。 69 | 70 | ※いくつかのソース(例:ローカルメディアソース)は、シーンが非アクティブの場合に送出が停止しますが、これはプラグインのバグではありません。 71 | 72 | # TIPS 73 | 74 | ## 1. 解像度を変更しつつレイアウト変更して配信したい 75 | 76 | > 解像度変更機能は 1.0.0 で統合されています。 77 | > 78 | > 以下の手順は、より柔軟性のある別の実現方法です(クロッピング、拡大、黒背景を追加等) 79 | 80 | 1. キャンバス解像度は 1080p であること前提とします。 81 | 2. 新たにブランクシーンを作成 82 | 3. 配信したいソースを右クリックしてメニューから「コピー」を実行 83 | 4. 1で作成したブランクシーンで右クリックしてメニューから「貼り付け(参照)」を実行。 84 | 必要に応じて拡大縮小、クロッピングしてください。 85 | 5. シーンのエフェクトフィルタに Branch Output を追加して配信設定 86 | 87 | 貼り付け(参照)の場合はソースを複製しないので、デバイス競合を起こすことはありません。 88 | 89 | Branch Output はシーンがアクティブでなくとも配信を行います。 90 | この方法はシーンがアクティブでないと再生されない一部のソース(例えばメディアソース)を除いてうまく動作するかと思います。 91 | 92 | ## 2. プログラムアウトを複数の配信プラットフォームに配信したい 93 | 94 | > **重要**: 映像のループを防ぐために、スタジオモードで作業してください。 95 | 96 | [Main View Source](https://obsproject.com/forum/resources/main-view-source.1501/) というプラグインを併用すると可能です。 97 | 98 | 1. 新たにブランクシーンを作成(配信用シーン) 99 | 2. 配信用シーンに Main View Source を追加する 100 | 4. シーンのエフェクトフィルタに Branch Output を追加して配信設定 101 | 102 | また、複数の配信用シーンを作成して、追加のソースを重ねれば、配信プラットフォームごとに内容を変更することができます。 103 | 例えば Twitch には Twitch のコメント、YouTube には YouTube のコメントを表示したい、という要件が該当します。 104 | 105 | ただし、この方法ではソースの追加はできますが、削除はできません 106 | 107 | # Development 108 | 109 | This plugin is developed under [obs-plugintemplate](https://github.com/obsproject/obs-plugintemplate) 110 | 111 | -------------------------------------------------------------------------------- /build-aux/.functions/log_debug: -------------------------------------------------------------------------------- 1 | if (( ! ${+_loglevel} )) typeset -g _loglevel=1 2 | 3 | if (( _loglevel > 2 )) print -PR -e -- "${CI:+::debug::}%F{220}DEBUG: ${@}%f" 4 | -------------------------------------------------------------------------------- /build-aux/.functions/log_error: -------------------------------------------------------------------------------- 1 | local icon=' ✖︎ ' 2 | 3 | print -u2 -PR "${CI:+::error::}%F{1} ${icon} %f ${@}" 4 | -------------------------------------------------------------------------------- /build-aux/.functions/log_group: -------------------------------------------------------------------------------- 1 | autoload -Uz log_info 2 | 3 | if (( ! ${+_log_group} )) typeset -g _log_group=0 4 | 5 | if (( ${+CI} )) { 6 | if (( _log_group )) { 7 | print "::endgroup::" 8 | typeset -g _log_group=0 9 | } 10 | if (( # )) { 11 | print "::group::${@}" 12 | typeset -g _log_group=1 13 | } 14 | } else { 15 | if (( # )) log_info ${@} 16 | } 17 | -------------------------------------------------------------------------------- /build-aux/.functions/log_info: -------------------------------------------------------------------------------- 1 | if (( ! ${+_loglevel} )) typeset -g _loglevel=1 2 | 3 | if (( _loglevel > 0 )) { 4 | local icon=' =>' 5 | 6 | print -PR "%F{4} ${(r:5:)icon}%f %B${@}%b" 7 | } 8 | -------------------------------------------------------------------------------- /build-aux/.functions/log_output: -------------------------------------------------------------------------------- 1 | if (( ! ${+_loglevel} )) typeset -g _loglevel=1 2 | 3 | if (( _loglevel > 0 )) { 4 | local icon='' 5 | 6 | print -PR " ${(r:5:)icon} ${@}" 7 | } 8 | -------------------------------------------------------------------------------- /build-aux/.functions/log_status: -------------------------------------------------------------------------------- 1 | if (( ! ${+_loglevel} )) typeset -g _loglevel=1 2 | 3 | if (( _loglevel > 0 )) { 4 | local icon=' >' 5 | 6 | print -PR "%F{2} ${(r:5:)icon}%f ${@}" 7 | } 8 | -------------------------------------------------------------------------------- /build-aux/.functions/log_warning: -------------------------------------------------------------------------------- 1 | if (( ! ${+_loglevel} )) typeset -g _loglevel=1 2 | 3 | if (( _loglevel > 0 )) { 4 | local icon=' =>' 5 | 6 | print -PR "${CI:+::warning::}%F{3} ${(r:5:)icon} ${@}%f" 7 | } 8 | -------------------------------------------------------------------------------- /build-aux/.functions/set_loglevel: -------------------------------------------------------------------------------- 1 | autoload -Uz log_debug log_error 2 | 3 | local -r _usage="Usage: %B${0}%b 4 | 5 | Set log level, following levels are supported: 0 (quiet), 1 (normal), 2 (verbose), 3 (debug)" 6 | 7 | if (( ! # )); then 8 | log_error 'Called without arguments.' 9 | log_output ${_usage} 10 | return 2 11 | elif (( ${1} >= 4 )); then 12 | log_error 'Called with loglevel > 3.' 13 | log_output ${_usage} 14 | fi 15 | 16 | typeset -g -i -r _loglevel=${1} 17 | log_debug "Log level set to '${1}'" 18 | -------------------------------------------------------------------------------- /build-aux/.run-format.zsh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | 3 | builtin emulate -L zsh 4 | setopt EXTENDED_GLOB 5 | setopt PUSHD_SILENT 6 | setopt ERR_EXIT 7 | setopt ERR_RETURN 8 | setopt NO_UNSET 9 | setopt PIPE_FAIL 10 | setopt NO_AUTO_PUSHD 11 | setopt NO_PUSHD_IGNORE_DUPS 12 | setopt FUNCTION_ARGZERO 13 | 14 | ## Enable for script debugging 15 | # setopt WARN_CREATE_GLOBAL 16 | # setopt WARN_NESTED_VAR 17 | # setopt XTRACE 18 | 19 | autoload -Uz is-at-least && if ! is-at-least 5.2; then 20 | print -u2 -PR "%F{1}${funcstack[1]##*/}:%f Running on Zsh version %B${ZSH_VERSION}%b, but Zsh %B5.2%b is the minimum supported version. Upgrade zsh to fix this issue." 21 | exit 1 22 | fi 23 | 24 | invoke_formatter() { 25 | if (( # < 1 )) { 26 | log_error "Usage invoke_formatter [formatter_name]" 27 | exit 2 28 | } 29 | 30 | case ${1} { 31 | clang) 32 | if (( ${+commands[clang-format-17]} )) { 33 | local formatter=clang-format-17 34 | } elif (( ${+commands[clang-format]} )) { 35 | local formatter=clang-format 36 | local -a formatter_version=($(clang-format --version)) 37 | 38 | if ! is-at-least 17.0.3 ${formatter_version[-1]}; then 39 | log_error "clang-format is not version 17.0.3 or above (found ${formatter_version[-1]}." 40 | exit 2 41 | fi 42 | 43 | if ! is-at-least ${formatter_version[-1]} 17.0.3; then 44 | log_error "clang-format is more recent than version 17.0.3 (found ${formatter_version[-1]})." 45 | exit 2 46 | fi 47 | } else { 48 | log_error "No viable clang-format version found (required 17.0.3)" 49 | exit 2 50 | } 51 | 52 | local -a source_files=(src/**/*.(c|cpp|h|hpp|m|mm)(.N)) 53 | 54 | local -a format_args=(-style=file -fallback-style=none) 55 | if (( _loglevel > 2 )) format_args+=(--verbose) 56 | ;; 57 | cmake) 58 | local formatter=cmake-format 59 | if (( ${+commands[cmake-format]} )) { 60 | local cmake_format_version=$(cmake-format --version) 61 | 62 | if ! is-at-least 0.6.13 ${cmake_format_version}; then 63 | log_error "cmake-format is not version 0.6.13 or above (found ${cmake_format_version})." 64 | exit 2 65 | fi 66 | } else { 67 | log_error "No viable cmake-format version found (required 0.6.13)" 68 | exit 2 69 | } 70 | 71 | local -a source_files=(**/(CMakeLists.txt|*.cmake)(.N)) 72 | source_files=(${source_files:#(build_*)/*}) 73 | 74 | local -a format_args=() 75 | if (( _loglevel > 2 )) format_args+=(--log-level debug) 76 | ;; 77 | swift) 78 | local formatter=swift-format 79 | if (( ${+commands[swift-format]} )) { 80 | local swift_format_version=$(swift-format --version) 81 | 82 | if ! is-at-least 508.0.0 ${swift_format_version}; then 83 | log_error "swift-format is not version 508.0.0 or above (found ${swift_format_version})." 84 | exit 2 85 | fi 86 | } else { 87 | log_error "No viable swift-format version found (required 508.0.0)" 88 | exit 2 89 | } 90 | 91 | local -a source_files=(**/*.swift(.N)) 92 | 93 | local -a format_args=() 94 | ;; 95 | *) log_error "Invalid formatter specified: ${1}. Valid options are clang-format, cmake-format, and swift-format."; exit 2 ;; 96 | } 97 | 98 | local file 99 | local -i num_failures=0 100 | if (( check_only )) { 101 | for file (${source_files}) { 102 | if (( _loglevel > 1 )) log_info "Checking format of ${file}..." 103 | 104 | if ! "${formatter}" ${format_args} "${file}" | diff -q "${file}" - &> /dev/null; then 105 | log_error "${file} requires formatting changes." 106 | 107 | if (( fail_on_error == 2 )) return 2; 108 | num_failures=$(( num_failures + 1 )) 109 | else 110 | if (( _loglevel > 1 )) log_status "${file} requires no formatting changes." 111 | fi 112 | } 113 | if (( fail_on_error && num_failures )) return 2; 114 | } elif (( ${#source_files} )) { 115 | format_args+=(-i) 116 | "${formatter}" ${format_args} ${source_files} 117 | } 118 | } 119 | 120 | run_format() { 121 | if (( ! ${+SCRIPT_HOME} )) typeset -g SCRIPT_HOME=${ZSH_ARGZERO:A:h} 122 | if (( ! ${+FORMATTER_NAME} )) typeset -g FORMATTER_NAME=${${(s:-:)ZSH_ARGZERO:t:r}[2]} 123 | 124 | typeset -g host_os=${${(L)$(uname -s)}//darwin/macos} 125 | local -i fail_on_error=0 126 | local -i check_only=0 127 | local -i verbosity=1 128 | local -r _version='1.0.0' 129 | 130 | fpath=("${SCRIPT_HOME}/.functions" ${fpath}) 131 | autoload -Uz log_info log_error log_output set_loglevel log_status log_warning 132 | 133 | local -r _usage=" 134 | Usage: %B${functrace[1]%:*}%b