commit 0e83ab9eeffb8e329eb3c0c4a2061e3d2e4d3a73 Author: Vulcast Date: Mon Jun 29 22:58:49 2026 +0100 Initial import from Aitum/obs-vertical-canvas v1.6.4 Forked for Vulcast branding changes. Licensed under GPL-2.0. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a530800 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +*~ +.DS_Store +/build/ +/build_*/ +/release/ +/installer/Output/ + +.vscode +.idea +.deps/ + +# ignore generated files +*.generated.* +**/.Brewfile.lock.json +version.h +cmake/.CMakeBuildNumber +.ccache.conf diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b82a80e --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,97 @@ +# --- Detect if the plugin is build out of tree or not --- +if(CMAKE_PROJECT_NAME STREQUAL "obs-studio") + set(BUILD_OUT_OF_TREE OFF) +else() + set(BUILD_OUT_OF_TREE ON) + cmake_minimum_required(VERSION 3.16...3.26) +endif() +include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/bootstrap.cmake" NO_POLICY_SCOPE) + +project(${_name} VERSION ${_version}) + +include(compilerconfig) +include(defaults) +include(helpers) + +add_library(${PROJECT_NAME} MODULE) + +target_link_libraries(${PROJECT_NAME} PRIVATE OBS::libobs) + +if(BUILD_OUT_OF_TREE) + find_package(libobs REQUIRED) + find_package(obs-frontend-api REQUIRED) + target_link_libraries(${PROJECT_NAME} PRIVATE OBS::obs-frontend-api) +else() + target_link_libraries(${PROJECT_NAME} PRIVATE OBS::frontend-api) +endif() + +find_package(Qt6 COMPONENTS Widgets Core) +if(BUILD_OUT_OF_TREE) + if(OS_LINUX OR OS_FREEBSD OR OS_OPENBSD) + find_package(Qt6 REQUIRED Gui) + endif() +endif() +target_link_libraries(${PROJECT_NAME} PRIVATE Qt::Core Qt::Widgets) + +if((OS_LINUX OR OS_FREEBSD OR OS_OPENBSD) AND Qt6_VERSION VERSION_LESS "6.9.0") + find_package(Qt6 COMPONENTS GuiPrivate) + target_link_libraries(${PROJECT_NAME} PRIVATE Qt::GuiPrivate) +endif() + +target_compile_options( +${PROJECT_NAME} PRIVATE $<$:-Wno-quoted-include-in-framework-header + -Wno-comma>) +set_target_properties(${PROJECT_NAME} PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") +set_target_properties( +${PROJECT_NAME} +PROPERTIES AUTOMOC ON + AUTOUIC ON + AUTORCC ON) + +if(BUILD_OUT_OF_TREE) + find_package(CURL REQUIRED) +endif() +target_link_libraries(${PROJECT_NAME} PRIVATE CURL::libcurl) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_SOURCE_DIR}/version.h) + +if(OS_WINDOWS) + configure_file(cmake/windows/resources/installer-Windows.iss.in "${CMAKE_CURRENT_BINARY_DIR}/installer-Windows.generated.iss") +endif() + +target_sources(${PROJECT_NAME} PRIVATE + vertical-canvas.cpp + scenes-dock.cpp + sources-dock.cpp + source-tree.cpp + transitions-dock.cpp + qt-display.cpp + projector.cpp + config-dialog.cpp + hotkey-edit.cpp + name-dialog.cpp + audio-wrapper-source.c + file-updater.c + multi-canvas-source.c + resources.qrc + vertical-canvas.hpp + scenes-dock.hpp + sources-dock.hpp + source-tree.hpp + transitions-dock.hpp + qt-display.hpp + projector.hpp + display-helpers.hpp + config-dialog.hpp + hotkey-edit.hpp + name-dialog.hpp + audio-wrapper-source.h + obs-websocket-api.h + file-updater.h + multi-canvas-source.h) + +if(BUILD_OUT_OF_TREE) + set_target_properties_plugin(${CMAKE_PROJECT_NAME} PROPERTIES OUTPUT_NAME ${_name}) +else() + set_target_properties_obs(${PROJECT_NAME} PROPERTIES FOLDER "plugins/aitum" PREFIX "") +endif() diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..7612409 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,190 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 22, + "patch": 0 + }, + "configurePresets": [ + { + "name": "template", + "hidden": true, + "cacheVariables": { + "ENABLE_FRONTEND_API": false, + "ENABLE_QT": false + } + }, + { + "name": "macos", + "displayName": "macOS Universal", + "description": "Build for macOS (Universal binary)", + "inherits": ["template"], + "binaryDir": "${sourceDir}/build_macos", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Darwin" + }, + "generator": "Xcode", + "warnings": {"dev": true, "deprecated": true}, + "cacheVariables": { + "QT_VERSION": "6", + "CMAKE_OSX_DEPLOYMENT_TARGET": "12.0", + "CODESIGN_IDENTITY": "$penv{CODESIGN_IDENT}", + "CODESIGN_TEAM": "$penv{APPLE_TEAM}" + } + }, + { + "name": "macos-ci", + "inherits": ["macos"], + "displayName": "macOS Universal CI build", + "description": "Build for macOS (Universal binary) for CI", + "generator": "Xcode", + "cacheVariables": { + "CMAKE_COMPILE_WARNING_AS_ERROR": true + } + }, + { + "name": "windows-x64", + "displayName": "Windows x64", + "description": "Build for Windows x64", + "inherits": ["template"], + "binaryDir": "${sourceDir}/build_x64", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + }, + "generator": "Visual Studio 17 2022", + "architecture": "x64", + "warnings": {"dev": true, "deprecated": true}, + "cacheVariables": { + "QT_VERSION": "6", + "CMAKE_SYSTEM_VERSION": "10.0.18363.657" + } + }, + { + "name": "windows-ci-x64", + "inherits": ["windows-x64"], + "displayName": "Windows x64 CI build", + "description": "Build for Windows x64 on CI", + "cacheVariables": { + "CMAKE_COMPILE_WARNING_AS_ERROR": true + } + }, + { + "name": "ubuntu-x86_64", + "displayName": "Ubuntu x86_64", + "description": "Build for Ubuntu x86_64", + "inherits": ["template"], + "binaryDir": "${sourceDir}/build_x86_64", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + }, + "generator": "Ninja", + "warnings": {"dev": true, "deprecated": true}, + "cacheVariables": { + "QT_VERSION": "6", + "CMAKE_BUILD_TYPE": "RelWithDebInfo" + } + }, + { + "name": "ubuntu-ci-x86_64", + "inherits": ["ubuntu-x86_64"], + "displayName": "Ubuntu x86_64 CI build", + "description": "Build for Ubuntu x86_64 on CI", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_COMPILE_WARNING_AS_ERROR": true + } + }, + { + "name": "linux-aarch64", + "displayName": "Linux aarch64", + "description": "Build for Linux aarch64", + "inherits": ["template"], + "binaryDir": "${sourceDir}/build_aarch64", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + }, + "generator": "Ninja", + "warnings": {"dev": true, "deprecated": true}, + "cacheVariables": { + "QT_VERSION": "6", + "CMAKE_BUILD_TYPE": "RelWithDebInfo" + } + }, + { + "name": "linux-ci-aarch64", + "inherits": ["linux-aarch64"], + "displayName": "Linux aarch64 CI build", + "description": "Build for Linux aarch64 on CI", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_COMPILE_WARNING_AS_ERROR": true + } + } + ], + "buildPresets": [ + { + "name": "macos", + "configurePreset": "macos", + "displayName": "macOS Universal", + "description": "macOS build for Universal architectures", + "configuration": "Release" + }, + { + "name": "macos-ci", + "configurePreset": "macos-ci", + "displayName": "macOS Universal CI", + "description": "macOS CI build for Universal architectures", + "configuration": "RelWithDebInfo" + }, + { + "name": "windows-x64", + "configurePreset": "windows-x64", + "displayName": "Windows x64", + "description": "Windows build for x64", + "configuration": "RelWithDebInfo" + }, + { + "name": "windows-ci-x64", + "configurePreset": "windows-ci-x64", + "displayName": "Windows x64 CI", + "description": "Windows CI build for x64 (RelWithDebInfo configuration)", + "configuration": "RelWithDebInfo" + }, + { + "name": "ubuntu-x86_64", + "configurePreset": "ubuntu-x86_64", + "displayName": "Linux x86_64", + "description": "Linux build for x86_64", + "configuration": "RelWithDebInfo" + }, + { + "name": "ubuntu-ci-x86_64", + "configurePreset": "ubuntu-ci-x86_64", + "displayName": "Linux x86_64 CI", + "description": "Linux CI build for x86_64", + "configuration": "RelWithDebInfo" + }, + { + "name": "linux-aarch64", + "configurePreset": "linux-aarch64", + "displayName": "Linux aarch64", + "description": "Linux build for aarch64", + "configuration": "RelWithDebInfo" + }, + { + "name": "linux-ci-aarch64", + "configurePreset": "linux-ci-aarch64", + "displayName": "Linux aarch64 CI", + "description": "Linux CI build for aarch64", + "configuration": "RelWithDebInfo" + } + ] +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..7e96159 --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# Vertical Canvas for OBS Studio + +Plugin for [OBS Studio](https://github.com/obsproject/obs-studio) to add vertical canvas by [![Aitum logo](media/aitum.png) Aitum](https://aitum.tv) + +# Support + +Currently supported distributions can be found at: + +* Aitum Website: https://aitum.tv/vertical +* GitHub: https://github.com/Aitum/obs-vertical-canvas +* Flatpak: https://github.com/flathub/com.obsproject.Studio.Plugin.VerticalCanvas + +For support, please see our troubleshooter: https://l.aitum.tv/vh + +If this does not solve your issue, please use the relevant channel on our [Discord server](https://aitum.tv/discord). + +# Build +- In-tree build + - Build OBS Studio: https://obsproject.com/wiki/Install-Instructions + - Check out this repository to UI/frontend-plugins/vertical-canvas + - Add `add_subdirectory(vertical-canvas)` to UI/frontend-plugins/CMakeLists.txt + - Rebuild OBS Studio +- Stand-alone build + - Verify that you have development files for OBS + - Check out this repository and run `cmake -S . -B build -DBUILD_OUT_OF_TREE=On && cmake --build build` + +# Translations +Please read [Translations](TRANSLATIONS.md) diff --git a/TRANSLATIONS.md b/TRANSLATIONS.md new file mode 100644 index 0000000..c900042 --- /dev/null +++ b/TRANSLATIONS.md @@ -0,0 +1,8 @@ +# Translations + +If you'd like to contribute a translation for the Aitum Vertical Plugin into your language, feel free to start a PR. + +We ask that the following terms are not translated however, mostly as they are "branding" for us and thus should be consistent between languages. + +- Aitum +- Backtrack diff --git a/audio-wrapper-source.c b/audio-wrapper-source.c new file mode 100644 index 0000000..edbea41 --- /dev/null +++ b/audio-wrapper-source.c @@ -0,0 +1,100 @@ +#include +#include "audio-wrapper-source.h" + +const char *audio_wrapper_get_name(void *type_data) +{ + UNUSED_PARAMETER(type_data); + return "transition_audio_wrapper"; +} + +void *audio_wrapper_create(obs_data_t *settings, obs_source_t *source) +{ + UNUSED_PARAMETER(settings); + struct audio_wrapper_info *audio_wrapper = bzalloc(sizeof(struct audio_wrapper_info)); + audio_wrapper->source = source; + return audio_wrapper; +} + +void audio_wrapper_destroy(void *data) +{ + bfree(data); +} + +bool audio_wrapper_render(void *data, uint64_t *ts_out, struct obs_source_audio_mix *audio, uint32_t mixers, size_t channels, + size_t sample_rate) +{ + UNUSED_PARAMETER(sample_rate); + struct audio_wrapper_info *aw = (struct audio_wrapper_info *)data; + obs_source_t *source = aw->target(aw->param); + if (!source) + return false; + + uint64_t timestamp = obs_source_get_audio_timestamp(source); + if (!timestamp) { + obs_source_release(source); + return false; + } + if (!aw->mixers) { + *ts_out = timestamp; + obs_source_release(source); + return true; + } + mixers &= aw->mixers(aw->param); + if (mixers == 0) { + *ts_out = timestamp; + obs_source_release(source); + return true; + } + struct obs_source_audio_mix child_audio; + obs_source_get_audio_mix(source, &child_audio); + for (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) { + if ((mixers & (1 << mix)) == 0) + continue; + + for (size_t ch = 0; ch < channels; ch++) { + float *out = audio->output[mix].data[ch]; + float *in = child_audio.output[mix].data[ch]; + float *end = in + AUDIO_OUTPUT_FRAMES; + while (in < end) + *(out++) += *(in++); + } + } + *ts_out = timestamp; + obs_source_release(source); + return true; +} + +static void audio_wrapper_enum_sources(void *data, obs_source_enum_proc_t enum_callback, void *param, bool active) +{ + UNUSED_PARAMETER(active); + struct audio_wrapper_info *aw = (struct audio_wrapper_info *)data; + obs_source_t *source = aw->target(aw->param); + if (!source) + return; + + enum_callback(aw->source, source, param); + + obs_source_release(source); +} + +void audio_wrapper_enum_active_sources(void *data, obs_source_enum_proc_t enum_callback, void *param) +{ + audio_wrapper_enum_sources(data, enum_callback, param, true); +} + +void audio_wrapper_enum_all_sources(void *data, obs_source_enum_proc_t enum_callback, void *param) +{ + audio_wrapper_enum_sources(data, enum_callback, param, false); +} + +struct obs_source_info audio_wrapper_source = { + .id = "vertical_audio_wrapper_source", + .type = OBS_SOURCE_TYPE_SCENE, + .output_flags = OBS_SOURCE_COMPOSITE | OBS_SOURCE_CAP_DISABLED, + .get_name = audio_wrapper_get_name, + .create = audio_wrapper_create, + .destroy = audio_wrapper_destroy, + .audio_render = audio_wrapper_render, + .enum_active_sources = audio_wrapper_enum_active_sources, + .enum_all_sources = audio_wrapper_enum_all_sources, +}; diff --git a/audio-wrapper-source.h b/audio-wrapper-source.h new file mode 100644 index 0000000..16e49ea --- /dev/null +++ b/audio-wrapper-source.h @@ -0,0 +1,17 @@ +#pragma once +#ifdef __cplusplus +extern "C" { +#endif + +struct audio_wrapper_info { + obs_source_t *source; + void *param; + obs_source_t *(*target)(void *param); + uint32_t (*mixers)(void *param); +}; + +extern struct obs_source_info audio_wrapper_source; + +#ifdef __cplusplus +}; +#endif diff --git a/build-aux/.functions/log_debug b/build-aux/.functions/log_debug new file mode 100644 index 0000000..6a477a1 --- /dev/null +++ b/build-aux/.functions/log_debug @@ -0,0 +1,3 @@ +if (( ! ${+_loglevel} )) typeset -g _loglevel=1 + +if (( _loglevel > 2 )) print -PR -e -- "${CI:+::debug::}%F{220}DEBUG: ${@}%f" diff --git a/build-aux/.functions/log_error b/build-aux/.functions/log_error new file mode 100644 index 0000000..f1c7b43 --- /dev/null +++ b/build-aux/.functions/log_error @@ -0,0 +1,3 @@ +local icon=' ✖︎ ' + +print -u2 -PR "${CI:+::error::}%F{1} ${icon} %f ${@}" diff --git a/build-aux/.functions/log_group b/build-aux/.functions/log_group new file mode 100644 index 0000000..7b6aca9 --- /dev/null +++ b/build-aux/.functions/log_group @@ -0,0 +1,16 @@ +autoload -Uz log_info + +if (( ! ${+_log_group} )) typeset -g _log_group=0 + +if (( ${+CI} )) { + if (( _log_group )) { + print "::endgroup::" + typeset -g _log_group=0 + } + if (( # )) { + print "::group::${@}" + typeset -g _log_group=1 + } +} else { + if (( # )) log_info ${@} +} diff --git a/build-aux/.functions/log_info b/build-aux/.functions/log_info new file mode 100644 index 0000000..d437c29 --- /dev/null +++ b/build-aux/.functions/log_info @@ -0,0 +1,7 @@ +if (( ! ${+_loglevel} )) typeset -g _loglevel=1 + +if (( _loglevel > 0 )) { + local icon=' =>' + + print -PR "%F{4} ${(r:5:)icon}%f %B${@}%b" +} diff --git a/build-aux/.functions/log_output b/build-aux/.functions/log_output new file mode 100644 index 0000000..4d9b52f --- /dev/null +++ b/build-aux/.functions/log_output @@ -0,0 +1,7 @@ +if (( ! ${+_loglevel} )) typeset -g _loglevel=1 + +if (( _loglevel > 0 )) { + local icon='' + + print -PR " ${(r:5:)icon} ${@}" +} diff --git a/build-aux/.functions/log_status b/build-aux/.functions/log_status new file mode 100644 index 0000000..950e681 --- /dev/null +++ b/build-aux/.functions/log_status @@ -0,0 +1,7 @@ +if (( ! ${+_loglevel} )) typeset -g _loglevel=1 + +if (( _loglevel > 0 )) { + local icon=' >' + + print -PR "%F{2} ${(r:5:)icon}%f ${@}" +} diff --git a/build-aux/.functions/log_warning b/build-aux/.functions/log_warning new file mode 100644 index 0000000..ceff982 --- /dev/null +++ b/build-aux/.functions/log_warning @@ -0,0 +1,7 @@ +if (( ! ${+_loglevel} )) typeset -g _loglevel=1 + +if (( _loglevel > 0 )) { + local icon=' =>' + + print -PR "${CI:+::warning::}%F{3} ${(r:5:)icon} ${@}%f" +} diff --git a/build-aux/.functions/set_loglevel b/build-aux/.functions/set_loglevel new file mode 100644 index 0000000..e32f4bb --- /dev/null +++ b/build-aux/.functions/set_loglevel @@ -0,0 +1,17 @@ +autoload -Uz log_debug log_error + +local -r _usage="Usage: %B${0}%b + +Set log level, following levels are supported: 0 (quiet), 1 (normal), 2 (verbose), 3 (debug)" + +if (( ! # )); then + log_error 'Called without arguments.' + log_output ${_usage} + return 2 +elif (( ${1} >= 4 )); then + log_error 'Called with loglevel > 3.' + log_output ${_usage} +fi + +typeset -g -i -r _loglevel=${1} +log_debug "Log level set to '${1}'" diff --git a/build-aux/.run-format.zsh b/build-aux/.run-format.zsh new file mode 100644 index 0000000..ab535a1 --- /dev/null +++ b/build-aux/.run-format.zsh @@ -0,0 +1,272 @@ +#!/usr/bin/env zsh + +builtin emulate -L zsh +setopt EXTENDED_GLOB +setopt PUSHD_SILENT +setopt ERR_EXIT +setopt ERR_RETURN +setopt NO_UNSET +setopt PIPE_FAIL +setopt NO_AUTO_PUSHD +setopt NO_PUSHD_IGNORE_DUPS +setopt FUNCTION_ARGZERO + +## Enable for script debugging +# setopt WARN_CREATE_GLOBAL +# setopt WARN_NESTED_VAR +# setopt XTRACE + +autoload -Uz is-at-least && if ! is-at-least 5.8; then + 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." + exit 1 +fi + +invoke_formatter() { + if (( # < 1 )) { + log_error "Usage invoke_formatter [formatter_name]" + exit 2 + } + + local formatter="${1}" + shift + local -a source_files=(${@}) + + case ${formatter} { + clang) + if (( ${+commands[clang-format-17]} )) { + local formatter=clang-format-17 + } elif (( ${+commands[clang-format]} )) { + local formatter=clang-format + } else { + log_error "No viable clang-format version found (required 17.0.3)" + exit 2 + } + + local -a formatter_version=($(${formatter} --version)) + + if ! is-at-least 17.0.3 ${formatter_version[-1]}; then + log_error "clang-format is not version 17.0.3 or above (found ${formatter_version[-1]}." + exit 2 + fi + + if ! is-at-least ${formatter_version[-1]} 17.0.3; then + log_error "clang-format is more recent than version 17.0.3 (found ${formatter_version[-1]})." + exit 2 + fi + + if (( ! #source_files )) source_files=(src/**/*.(c|cpp|h|hpp|m|mm)(.N)) + + local -a format_args=(-style=file -fallback-style=none) + if (( _loglevel > 2 )) format_args+=(--verbose) + + check_files() { + local -i num_failures=0 + local -a source_files=($@) + local file + local -a format_args=(-style=file -fallback-style=none) + if (( _loglevel > 2 )) format_args+=(--verbose) + + local -a command=(${formatter} ${format_args}) + + for file (${source_files}) { + if ! ${command} "${file}" | diff -q "${file}" - &> /dev/null; then + log_error "${file} requires formatting changes." + if (( fail_on_error == 2 )) return 2; + num_failures=$(( num_failures + 1 )) + fi + } + if (( num_failures && fail_on_error == 1 )) return 2 + } + + format_files() { + local -a source_files=($@) + + if (( ${#source_files} )) { + local -a format_args=(-style=file -fallback-style=none -i) + if (( _loglevel > 2 )) format_args+=(--verbose) + + "${formatter}" ${format_args} ${source_files} + } + } + ;; + gersemi) + local formatter=gersemi + if (( ${+commands[gersemi]} )) { + local gersemi_version=($(gersemi --version)) + + if ! is-at-least 0.12.0 ${gersemi_version[2]}; then + log_error "gersemi is not version 0.12.0 or above (found ${gersemi_version[2]}." + exit 2 + fi + } + + if (( ! #source_files )) source_files=(CMakeLists.txt (cmake)/**/(CMakeLists.txt|*.cmake)(.N)) + + check_files() { + local -i num_failures=0 + local -a source_files=($@) + local file + local -a command=(${formatter} -c --no-cache ${source_files}) + + if (( ${#source_files} )) { + while read -r line; do + local -a line_tokens=(${(z)line}) + if (( #line_tokens )) { + file=${line_tokens[1]//*${project_root}\//} + + log_error "${file} requires formatting changes." + } else { + log_error "${line}" + } + + if (( fail_on_error == 2 )) return 2 + num_failures=$(( num_failures + 1 )) + done < <(${command} 2>&1) + + if (( num_failures && fail_on_error == 1 )) return 2 + } + } + + format_files() { + local -a source_files=($@) + + if (( ${#source_files} )) { + "${formatter}" -i ${source_files} + } + } + ;; + swift) + local formatter=swift-format + if (( ${+commands[swift-format]} )) { + local swift_format_version=$(swift-format --version) + + if ! is-at-least 508.0.0 ${swift_format_version}; then + log_error "swift-format is not version 508.0.0 or above (found ${swift_format_version})." + exit 2 + fi + } else { + log_error "No viable swift-format version found (required 508.0.0)" + exit 2 + } + + if (( ! #source_files )) source_files=(src/**/*.swift(.N)) + + check_files() { + local -i num_failures=0 + local -a source_files=($@) + local file + local -a format_args=() + + local -a command=(${formatter} ${format_args}) + + for file (${source_files}) { + if ! "${command}" "${file}" | diff -q "${file}" - &> /dev/null; then + log_error "${file} requires formatting changes." + if (( fail_on_error == 2 )) return 2; + num_failures=$(( num_failures + 1 )) + fi + } + if (( num_failures && fail_on_error == 1 )) return 2 + } + + format_files() { + local -a source_files=($@) + + if (( ${#source_files} )) { + local -a format_args=(-i) + + "${formatter}" ${format_args} ${source_files} + } + } + ;; + *) log_error "Invalid formatter specified: ${1}. Valid options are clang-format, gersemi, and swift-format."; exit 2 ;; + } + + + local file + local -i num_failures=0 + if (( check_only )) { + if (( ${+functions[check_files]} )) { + check_files ${source_files} + } else { + log_error "No format check function defined for formatter '${formatter}'" + exit 2 + } + } else { + if (( ${+functions[format_files]} )) { + format_files ${source_files} + } else { + log_error "No format function defined for formatter '${formatter}'" + exit 2 + } + } +} + +run_format() { + if (( ! ${+SCRIPT_HOME} )) typeset -g SCRIPT_HOME=${ZSH_ARGZERO:A:h} + if (( ! ${+FORMATTER_NAME} )) typeset -g FORMATTER_NAME=${${(s:-:)ZSH_ARGZERO:t:r}[2]} + local project_root=${SCRIPT_HOME:A:h} + + typeset -g host_os=${${(L)$(uname -s)}//darwin/macos} + local -i fail_on_error=0 + local -i check_only=0 + local -i verbosity=1 + local -r _version='2.0.0' + + fpath=("${SCRIPT_HOME}/.functions" ${fpath}) + autoload -Uz log_info log_error log_output set_loglevel log_status log_warning + + local -r _usage=" +Usage: %B${functrace[1]%:*}%b