pax_global_header00006660000000000000000000000064141502574520014517gustar00rootroot0000000000000052 comment=340b566f9f66b731eec39f42307578fef6be7283 obs-text-slideshow-1.5.2/000077500000000000000000000000001415025745200152705ustar00rootroot00000000000000obs-text-slideshow-1.5.2/.clang-format000066400000000000000000000063301415025745200176450ustar00rootroot00000000000000# please use clang-format version 8 or later Standard: Cpp11 AccessModifierOffset: -8 AlignAfterOpenBracket: Align AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignEscapedNewlines: Left AlignOperands: true AlignTrailingComments: true #AllowAllArgumentsOnNextLine: false # requires clang-format 9 #AllowAllConstructorInitializersOnNextLine: false # requires clang-format 9 AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: Inline AllowShortIfStatementsOnASingleLine: false #AllowShortLambdasOnASingleLine: Inline # requires clang-format 9 AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: false BinPackArguments: true BinPackParameters: true BraceWrapping: AfterClass: false AfterControlStatement: false AfterEnum: false AfterFunction: true AfterNamespace: false AfterObjCDeclaration: false AfterStruct: false AfterUnion: false AfterExternBlock: false BeforeCatch: false BeforeElse: false IndentBraces: false SplitEmptyFunction: true SplitEmptyRecord: true SplitEmptyNamespace: true BreakBeforeBinaryOperators: None BreakBeforeBraces: Custom BreakBeforeTernaryOperators: true BreakConstructorInitializers: BeforeColon BreakStringLiterals: false # apparently unpredictable ColumnLimit: 80 CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 8 ContinuationIndentWidth: 8 Cpp11BracedListStyle: true DerivePointerAlignment: false DisableFormat: false FixNamespaceComments: false ForEachMacros: - 'json_object_foreach' - 'json_object_foreach_safe' - 'json_array_foreach' IncludeBlocks: Preserve IndentCaseLabels: false IndentPPDirectives: None IndentWidth: 8 IndentWrappedFunctionNames: false KeepEmptyLinesAtTheStartOfBlocks: true MaxEmptyLinesToKeep: 1 NamespaceIndentation: None #ObjCBinPackProtocolList: Auto # requires clang-format 7 ObjCBlockIndentWidth: 8 ObjCSpaceAfterProperty: true ObjCSpaceBeforeProtocolList: true PenaltyBreakAssignment: 10 PenaltyBreakBeforeFirstCallParameter: 30 PenaltyBreakComment: 10 PenaltyBreakFirstLessLess: 0 PenaltyBreakString: 10 PenaltyExcessCharacter: 100 PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Right ReflowComments: false SortIncludes: false SortUsingDeclarations: false SpaceAfterCStyleCast: false #SpaceAfterLogicalNot: false # requires clang-format 9 SpaceAfterTemplateKeyword: false SpaceBeforeAssignmentOperators: true #SpaceBeforeCtorInitializerColon: true # requires clang-format 7 #SpaceBeforeInheritanceColon: true # requires clang-format 7 SpaceBeforeParens: ControlStatements #SpaceBeforeRangeBasedForLoopColon: true # requires clang-format 7 SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInCStyleCastParentheses: false SpacesInContainerLiterals: false SpacesInParentheses: false SpacesInSquareBrackets: false #StatementMacros: # requires clang-format 8 # - 'Q_OBJECT' TabWidth: 8 #TypenameMacros: # requires clang-format 9 # - 'DARRAY' UseTab: ForContinuationAndIndentation --- Language: ObjC obs-text-slideshow-1.5.2/.github/000077500000000000000000000000001415025745200166305ustar00rootroot00000000000000obs-text-slideshow-1.5.2/.github/ISSUE_TEMPLATE/000077500000000000000000000000001415025745200210135ustar00rootroot00000000000000obs-text-slideshow-1.5.2/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000013431415025745200235060ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: "[BUG]" labels: bug assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - OS Version: [e.g. Windows, Ubuntu 20.04, macOS 10.14, ...] - OBS version - GDI+ Text Slide Show or Free Type 2 Text Slide Show or both **Additional context** Add any other context about the problem here. obs-text-slideshow-1.5.2/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000011341415025745200245370ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project title: '' labels: enhancement assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. obs-text-slideshow-1.5.2/.github/workflows/000077500000000000000000000000001415025745200206655ustar00rootroot00000000000000obs-text-slideshow-1.5.2/.github/workflows/clang-format.yml000066400000000000000000000006511415025745200237640ustar00rootroot00000000000000name: Clang Format Check on: [push, pull_request] jobs: check_format: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 with: submodules: 'recursive' - name: Install clang format run: | sudo apt-get install -y clang-format-10 - name: Check the Formatting run: | ./formatcode.sh ./ci/check-format.sh obs-text-slideshow-1.5.2/.github/workflows/main.yml000066400000000000000000000163411415025745200223410ustar00rootroot00000000000000name: 'CI Multiplatform Build' on: [push, pull_request] jobs: ubuntu64: name: 'Build_linux' runs-on: [ubuntu-18.04] steps: - name: 'Checkout' uses: actions/checkout@v2.3.3 - name: 'Install dependencies' shell: bash working-directory: ${{ github.workspace }} run: ./ci/linux/install-dependencies-ubuntu.sh - name: 'Build Plugin' shell: bash working-directory: ${{ github.workspace }} run: ./ci/linux/build-ubuntu.sh - name: 'Package Plugin' shell: bash working-directory: ${{ github.workspace }} run: ./ci/linux/package-ubuntu.sh macos: name: 'Build_macos' runs-on: [macos-latest] steps: - name: 'Checkout' uses: actions/checkout@v2.3.3 - name: 'Install dependencies' shell: bash working-directory: ${{ github.workspace }} run: ./ci/macos/install-dependencies-macos.sh - name: 'Build OBS' shell: bash working-directory: ${{ github.workspace }} run: ./ci/macos/install-build-obs-macos.sh - name: 'Build Plugin' shell: bash working-directory: ${{ github.workspace }} run: ./ci/macos/build-macos.sh win64: name: 'Build_win64' runs-on: [windows-latest] env: QT_VERSION: '5.15.2' CMAKE_GENERATOR: "Visual Studio 16 2019" CMAKE_SYSTEM_VERSION: "10.0.18363.657" WINDOWS_DEPS_VERSION: '2019' WINDOWS_DEPS_CACHE_VERSION: '1' steps: - name: 'Add msbuild to PATH' uses: microsoft/setup-msbuild@v1.0.2 - name: 'Checkout' uses: actions/checkout@v2.3.3 - name: 'Restore QT dependency from cache' id: qt-cache uses: actions/cache@v2.1.2 env: CACHE_NAME: 'windows-qt-cache' with: path: ${{ github.workspace }}/cmbuild/QT key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.QT_VERSION }} - name: 'Install Qt' shell: bash if: steps.qt-cache.outputs.cache-hit != 'true' run: | curl -kLO https://cdn-fastly.obsproject.com/downloads/Qt_${{ env.QT_VERSION }}.7z -f --retry 5 -C - 7z x Qt_${{ env.QT_VERSION }}.7z -o"${{ github.workspace }}/cmbuild/QT" - name: 'Restore pre-built dependencies from cache' id: deps-cache uses: actions/cache@v2.1.2 env: CACHE_NAME: 'windows-deps-cache' with: path: ${{ github.workspace }}/cmbuild/deps key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.WINDOWS_DEPS_VERSION }}-${{ env.WINDOWS_DEPS_CACHE_VERSION }} - name: 'Install prerequisite: Pre-built dependencies' if: steps.deps-cache.outputs.cache-hit != 'true' run: | curl -kLO https://cdn-fastly.obsproject.com/downloads/dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -f --retry 5 -C - 7z x dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -o"${{ github.workspace }}/cmbuild/deps" - name: 'Build OBS' run: | git clone https://github.com/obsproject/obs-studio cd obs-studio mkdir build64 cd build64 cmake -G"${{ env.CMAKE_GENERATOR }}" -A"x64" -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DDISABLE_PLUGINS=true -DDepsPath="${{ github.workspace }}/cmbuild/deps/win64" -DQTDIR="${{ github.workspace }}/cmbuild/QT/${{ env.QT_VERSION }}/msvc2019_64" -DCOPIED_DEPENDENCIES=FALSE -DCOPY_DEPENDENCIES=TRUE .. msbuild /m /p:Configuration=RelWithDebInfo .\obs-studio.sln - name: 'Build Plugin' working-directory: ${{ github.workspace }} run: | mkdir build64 cd build64 cmake -G"${{ env.CMAKE_GENERATOR }}" -A"x64" -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}/cmbuild/QT/${{ env.QT_VERSION }}/msvc2019_64" -DLibObs_DIR="${{ github.workspace }}\obs-studio\build64\libobs" -DLIBOBS_INCLUDE_DIR="${{ github.workspace }}\obs-studio\libobs" -DLIBOBS_LIB="${{ github.workspace }}\obs-studio\build64\libobs\RelWithDebInfo\obs.lib" -DOBS_FRONTEND_LIB="${{ github.workspace }}\obs-studio\build64\UI\obs-frontend-api\RelWithDebInfo\obs-frontend-api.lib" .. msbuild /m /p:Configuration=RelWithDebInfo .\obs-text-slideshow.sln win32: name: 'Build_win32' runs-on: [windows-latest] env: QT_VERSION: '5.15.2' CMAKE_GENERATOR: "Visual Studio 16 2019" CMAKE_SYSTEM_VERSION: "10.0.18363.657" WINDOWS_DEPS_VERSION: '2019' WINDOWS_DEPS_CACHE_VERSION: '1' steps: - name: 'Add msbuild to PATH' uses: microsoft/setup-msbuild@v1.0.2 - name: 'Checkout' uses: actions/checkout@v2.3.3 - name: 'Restore QT dependency from cache' id: qt-cache uses: actions/cache@v2.1.2 env: CACHE_NAME: 'windows-qt-cache' with: path: ${{ github.workspace }}/cmbuild/QT key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.QT_VERSION }} - name: 'Install Qt' shell: bash if: steps.qt-cache.outputs.cache-hit != 'true' run: | curl -kLO https://cdn-fastly.obsproject.com/downloads/Qt_${{ env.QT_VERSION }}.7z -f --retry 5 -C - 7z x Qt_${{ env.QT_VERSION }}.7z -o"${{ github.workspace }}/cmbuild/QT" - name: 'Restore pre-built dependencies from cache' id: deps-cache uses: actions/cache@v2.1.2 env: CACHE_NAME: 'windows-deps-cache' with: path: ${{ github.workspace }}/cmbuild/deps key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.WINDOWS_DEPS_VERSION }}-${{ env.WINDOWS_DEPS_CACHE_VERSION }} - name: 'Install prerequisite: Pre-built dependencies' if: steps.deps-cache.outputs.cache-hit != 'true' run: | curl -kLO https://cdn-fastly.obsproject.com/downloads/dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -f --retry 5 -C - 7z x dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -o"${{ github.workspace }}/cmbuild/deps" - name: 'Build OBS' run: | git clone https://github.com/obsproject/obs-studio cd obs-studio mkdir build32 cd build32 cmake -G"${{ env.CMAKE_GENERATOR }}" -A"Win32" -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DDISABLE_PLUGINS=true -DDepsPath="${{ github.workspace }}/cmbuild/deps/win32" -DQTDIR="${{ github.workspace }}/cmbuild/QT/${{ env.QT_VERSION }}/msvc2019" -DCOPIED_DEPENDENCIES=FALSE -DCOPY_DEPENDENCIES=TRUE .. msbuild /m /p:Configuration=RelWithDebInfo .\obs-studio.sln - name: 'Build Plugin' working-directory: ${{ github.workspace }} run: | mkdir build32 cd build32 cmake -G"${{ env.CMAKE_GENERATOR }}" -A"Win32" -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}/cmbuild/QT/${{ env.QT_VERSION }}/msvc2019" -DLibObs_DIR="${{ github.workspace }}\obs-studio\build32\libobs" -DLIBOBS_INCLUDE_DIR="${{ github.workspace }}\obs-studio\libobs" -DLIBOBS_LIB="${{ github.workspace }}\obs-studio\build32\libobs\RelWithDebInfo\obs.lib" -DOBS_FRONTEND_LIB="${{ github.workspace }}\obs-studio\build32\UI\obs-frontend-api\RelWithDebInfo\obs-frontend-api.lib" .. msbuild /m /p:Configuration=RelWithDebInfo .\obs-text-slideshow.sln obs-text-slideshow-1.5.2/.gitignore000066400000000000000000000002051415025745200172550ustar00rootroot00000000000000*~ .DS_Store /build/ /build32/ /build64/ /release/ /installer/Output/ .vscode .idea # ignore generated files *.generated.* package obs-text-slideshow-1.5.2/CMakeLists.txt000066400000000000000000000171171415025745200200370ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10) # Change obs-plugintemplate to your plugin's name in a machine-readable format # (e.g.: obs-myawesomeplugin) and set project(obs-text-slideshow VERSION 1.5.2) # Replace `Your Name Here` with the name (yours or your organization's) you want # to see as the author of the plugin (in the plugin's metadata itself and in the installers) set(PLUGIN_AUTHOR "Joshua Wong") # Replace `com.example.obs-plugin-template` with a unique Bundle ID for macOS releases # (used both in the installer and when submitting the installer for notarization) set(MACOS_BUNDLEID "com.example.obs-text-slideshow") # Replace `me@contoso.com` with the maintainer email address you want to put in Linux packages set(LINUX_MAINTAINER_EMAIL "jbwong05@gmail.com") # TAKE NOTE: No need to edit things past this point set(CMAKE_PREFIX_PATH "${QTDIR}") set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) # In case you need C++ set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) if (WIN32 OR APPLE) include(external/FindLibObs.cmake) endif() find_package(LibObs REQUIRED) find_package(Qt5 REQUIRED COMPONENTS Core Widgets) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() configure_file( src/plugin-macros.h.in ../src/plugin-macros.generated.h ) configure_file( installer/installer-macOS.pkgproj.in ../installer/installer-macOS.generated.pkgproj ) configure_file( installer/installer-Windows.iss.in ../installer/installer-Windows.generated.iss ) configure_file( ci/ci_includes.sh.in ../ci/ci_includes.generated.sh ) configure_file( ci/ci_includes.cmd.in ../ci/ci_includes.generated.cmd ) set(PLUGIN_SOURCES src/plugin-main.cpp src/obs-text-freetype2-slideshow.cpp src/obs-text-slideshow.cpp src/obs-text-slideshow-dock.cpp src/files.cpp) if(WIN32) set(PLUGIN_SOURCES ${PLUGIN_SOURCES} src/obs-text-gdiplus-slideshow.cpp) endif() set(PLUGIN_HEADERS src/plugin-macros.generated.h src/obs-text-slideshow.h src/obs-text-slideshow-dock.h src/files.h) qt5_wrap_ui(PLUGIN_UI_HEADERS src/obs-text-slideshow-dock.ui) # --- Platform-independent build settings --- add_library(${CMAKE_PROJECT_NAME} MODULE ${PLUGIN_SOURCES} ${PLUGIN_HEADERS} ${PLUGIN_UI_HEADERS}) include_directories( "${LIBOBS_INCLUDE_DIR}/../UI/obs-frontend-api" ${Qt5Core_INCLUDES} ${Qt5Widgets_INCLUDES} ) target_link_libraries(${CMAKE_PROJECT_NAME} libobs Qt5::Core Qt5::Widgets ) # --- End of section --- # --- Windows-specific build settings and tasks --- if(WIN32) if(NOT DEFINED OBS_FRONTEND_LIB) set(OBS_FRONTEND_LIB "OBS_FRONTEND_LIB-NOTFOUND" CACHE FILEPATH "OBS frontend library") message(FATAL_ERROR "Could not find OBS Frontend API\'s library !") endif() # Enable Multicore Builds and disable FH4 (to not depend on VCRUNTIME140_1.DLL when building with VS2019) if (MSVC) add_definitions(/MP /d2FH4-) endif() if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(ARCH_NAME "64bit") set(OBS_BUILDDIR_ARCH "build64") if(MSVC) # Hardcoded path to w32-pthreads atm because find_library not properly finding w32-pthreads.lib target_link_libraries( ${CMAKE_PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/obs-studio/build64/deps/w32-pthreads/RelWithDebInfo/w32-pthreads.lib) endif() else() set(ARCH_NAME "32bit") set(OBS_BUILDDIR_ARCH "build32") if(MSVC) # Hardcoded path to w32-pthreads atm because find_library not properly finding w32-pthreads.lib target_link_libraries( ${CMAKE_PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/obs-studio/build32/deps/w32-pthreads/RelWithDebInfo/w32-pthreads.lib) endif() endif() include_directories( "${LIBOBS_INCLUDE_DIR}/../${OBS_BUILDDIR_ARCH}/UI" ) target_link_libraries(${CMAKE_PROJECT_NAME} "${OBS_FRONTEND_LIB}" ) # --- Release package helper --- # The "release" folder has a structure similar OBS' one on Windows set(RELEASE_DIR "${PROJECT_SOURCE_DIR}/release") add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD # If config is Release or RelWithDebInfo, package release files COMMAND if $,$>==1 ( "${CMAKE_COMMAND}" -E make_directory "${RELEASE_DIR}/data/obs-plugins/${CMAKE_PROJECT_NAME}" "${RELEASE_DIR}/obs-plugins/${ARCH_NAME}" ) COMMAND if $,$>==1 ( "${CMAKE_COMMAND}" -E copy_directory "${PROJECT_SOURCE_DIR}/data" "${RELEASE_DIR}/data/obs-plugins/${CMAKE_PROJECT_NAME}" ) COMMAND if $,$>==1 ( "${CMAKE_COMMAND}" -E copy "$" "${RELEASE_DIR}/obs-plugins/${ARCH_NAME}" ) # If config is RelWithDebInfo, copy the pdb file COMMAND if $==1 ( "${CMAKE_COMMAND}" -E copy "$" "${RELEASE_DIR}/obs-plugins/${ARCH_NAME}" ) # Copy to obs-studio dev environment for immediate testing COMMAND if $==1 ( "${CMAKE_COMMAND}" -E copy "$" "${LIBOBS_INCLUDE_DIR}/../${OBS_BUILDDIR_ARCH}/rundir/$/obs-plugins/${ARCH_NAME}" ) COMMAND if $==1 ( "${CMAKE_COMMAND}" -E copy "$" "${LIBOBS_INCLUDE_DIR}/../${OBS_BUILDDIR_ARCH}/rundir/$/obs-plugins/${ARCH_NAME}" ) COMMAND if $==1 ( "${CMAKE_COMMAND}" -E make_directory "${LIBOBS_INCLUDE_DIR}/../${OBS_BUILDDIR_ARCH}/rundir/$/data/obs-plugins/${CMAKE_PROJECT_NAME}" ) COMMAND if $==1 ( "${CMAKE_COMMAND}" -E copy_directory "${PROJECT_SOURCE_DIR}/data" "${LIBOBS_INCLUDE_DIR}/../${OBS_BUILDDIR_ARCH}/rundir/$/data/obs-plugins/${CMAKE_PROJECT_NAME}" ) ) # --- End of sub-section --- endif() # --- End of section --- # --- Linux-specific build settings and tasks --- if(UNIX OR APPLE) if(${CMAKE_BUILD_TYPE} STREQUAL Debug) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Og") else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") endif() endif() if(UNIX AND NOT APPLE) include(GNUInstallDirs) set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES PREFIX "") target_link_libraries(${CMAKE_PROJECT_NAME} obs-frontend-api) file(GLOB locale_files data/locale/*.ini) if(${BUILD_UBUNTU}) install(TARGETS ${CMAKE_PROJECT_NAME} LIBRARY DESTINATION "/usr/lib/obs-plugins") install(FILES ${locale_files} DESTINATION "/usr/share/obs/obs-plugins/${CMAKE_PROJECT_NAME}/locale") else() install(TARGETS ${CMAKE_PROJECT_NAME} LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/obs-plugins") install(FILES ${locale_files} DESTINATION "${CMAKE_INSTALL_FULL_DATAROOTDIR}/obs/obs-plugins/${CMAKE_PROJECT_NAME}/locale") endif() endif() # --- End of section --- # -- OS X specific build settings and tasks -- if(APPLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -fvisibility=default") set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES PREFIX "") target_link_libraries(${CMAKE_PROJECT_NAME} "${OBS_FRONTEND_LIB}") endif() # -- End of section -- obs-text-slideshow-1.5.2/CONTRIBUTING.md000066400000000000000000000003431415025745200175210ustar00rootroot00000000000000# Contributing 1. Fork the repository 2. Switch to a new branch 3. Commit your changes (Make sure to use descriptive commit messages and to squash your commits to avoid redundant and unnecessary commits) 5. Open a pull request obs-text-slideshow-1.5.2/LICENSE000066400000000000000000000431751415025745200163070ustar00rootroot00000000000000 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. {description} Copyright (C) {year} {fullname} 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. {signature of Ty Coon}, 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.obs-text-slideshow-1.5.2/README.md000066400000000000000000000154621415025745200165570ustar00rootroot00000000000000# obs-text-slideshow [![latest-tag](https://badgen.net/github/release/jbwong05/obs-text-slideshow)](https://github.com/jbwong05/obs-text-slideshow) [![Downloads](https://img.shields.io/github/downloads/jbwong05/obs-text-slideshow/total?cacheSeconds=3600)](https://github.com/jbwong05/obs-text-slideshow/releases) ![CI Multiplatform Build](https://github.com/jbwong05/obs-text-slideshow/actions/workflows/main.yml/badge.svg) ![Clang Format](https://github.com/jbwong05/obs-text-slideshow/actions/workflows/clang-format.yml/badge.svg) Inspired by the built in [image slideshow](https://github.com/obsproject/obs-studio/blob/master/plugins/image-source/obs-slideshow.c), except for text sources instead. Both Free Type 2 and GDI+ are supported. Useful for displaying song lyrics, captions, etc. ## Usage This plugin functions exactly as the built in [image slideshow](https://github.com/obsproject/obs-studio/blob/master/plugins/image-source/obs-slideshow.c) plugin, except instead of transitioning between image sources it transitions between text sources instead. Simply click the add source button and add the Text Slide Show source. Text sources can be added manually using the default `Slide Texts` box or read from file by selecting the read from file option and specifying the file path(s). The expected format of the text file(s) is described [below](https://github.com/jbwong05/obs-text-slideshow#text-file-format). Transitioning between text sources manually can be done using OBS's media controls or using the built in [dock](https://github.com/jbwong05/obs-text-slideshow#dock). ### Text file format #### Single text file format By default, if no custom delimiter is specified, the new line character is chosen as the delimiter as follows: ``` text for first text source text for second text source text for third text source single line is supported multiline is supported etc. ``` Custom delimiters can also be specified so other characters can be used as delimiters instead of the new line character. #### Multiple text file format For the multiple text file option, the text from each text file corresponds to the text for one text source in the slideshow. ### Dock To open the dock, Select View -> Docks -> OBS Text Slide Show. The dock will display the first Text Slide Show source for the current scene as well as all of its text sources below. Clicking on the text sources on the dock will cause the source to transition to this text source. The drop down at the top of the dock can be used to have the dock display another Text Slide Show on the current scene other than the first. Only Text Slide Show sources on the current scene can be viewed on the dock. When the current scene is changed, the dock and will update accordingly to include only the Text Slide Show sources on the new scene. ## Installation Installers can be found in the [Releases](https://github.com/jbwong05/obs-text-slideshow/releases) section. Note: The Windows and macOS installers are currently unsigned because I don't have money to purchase a certificate to sign the Windows installer or to purchase a subscription for the Apple Developer program in order to sign and notarize the macOS pkg installer. Unfortunately as a result these installers may be flagged by certain antivirus programs and may have to be temporarily disabled in order for the installer to be allowed to run. ## Building from Source ### Prerequisites You'll need CMake and a minimal working development environment for OBS Studio installed on your computer. Only Qt and the standard `obs dependencies`(https://github.com/obsproject/obs-deps) are required. obs can be built with the `-DDISABLE_PLUGINS=true` flag which is sufficient. More specific details can be found [here](https://github.com/obsproject/obs-studio/wiki/Install-Instructions#windows-build-directions). ### Windows #### Building with cmake-gui In cmake-gui, you'll have to set these CMake variables: - **QTDIR** (path) : location of the Qt environment suited for your compiler and architecture - **LIBOBS_INCLUDE_DIR** (path) : location of the `libobs` subfolder in the source code of OBS Studio - **LIBOBS_LIB** (filepath) : location of the obs.lib file - **OBS_FRONTEND_LIB** (filepath) : location of the obs-frontend-api.lib file - **LibObs_DIR** (filepath) : location of `libobs` folder within the obs build directory #### Building from Command Line ``` git clone https://github.com/jbwong05/obs-text-slideshow.git cd obs-text-slideshow mkdir build && cd build # Windows 64-bit cmake -G"Visual Studio 16 2019" -A"x64" -DCMAKE_SYSTEM_VERSION="10.0.18363.657" -DQTDIR= -DLibObs_DIR= -DLIBOBS_INCLUDE_DIR= -DLIBOBS_LIB= -DOBS_FRONTEND_LIB= .. # or Windows 32-bit cmake -G"Visual Studio 16 2019" -A"Win32" -DCMAKE_SYSTEM_VERSION="10.0.18363.657" -DQTDIR= -DLibObs_DIR= -DLIBOBS_INCLUDE_DIR= -DLIBOBS_LIB= -DOBS_FRONTEND_LIB= .. # Open `obs-text-slideshow.sln` with Visual Studio and build # Copy `obs-text-slideshow.dll` and `obs-text-slideshow.pdb` to the obs plugin directory # Create a obs-text-slideshow folder in the obs data directory and copy the locale folder into the new obs-text-slideshow directory ``` ### Linux ``` git clone https://github.com/jbwong05/obs-text-slideshow.git cd obs-text-slideshow mkdir build && cd build # If you are on Ubuntu, add the `-DBUILD_UBUNTU=true` flag to your cmake command cmake .. # If dependencies are not on your path, you can manually specify their paths with the following: cmake -DQTDIR= -DLIBOBS_INCLUDE_DIR= -DLIBOBS_LIB= -DOBS_FRONTEND_LIB= .. make sudo make install ``` ### OS X ``` git clone https://github.com/jbwong05/obs-text-slideshow.git cd obs-text-slideshow mkdir build && cd build cmake -DLIBOBS_INCLUDE_DIR= -DLIBOBS_LIB= -DOBS_FRONTEND_LIB= -DQTDIR= .. make # Copy libobs-text-slideshow.so to the obs-plugins folder # Create a obs-text-slideshow folder in the obs data directory and copy the locale folder into the new obs-text-slideshow directory ``` ## Possible future work - [x] Text input from files (UTF-8) - [ ] Individual text settings for each text source - [x] GUI dock for easier transitioning between sources - [x] Find work around for the need for a `refresh sources` button; ~~because the obs-frontend-api doesn't have a source added or edited event~~ libobs handles the different signals for sources and not the frontend apiobs-text-slideshow-1.5.2/ci/000077500000000000000000000000001415025745200156635ustar00rootroot00000000000000obs-text-slideshow-1.5.2/ci/check-format.sh000077500000000000000000000003561415025745200205710ustar00rootroot00000000000000#!/bin/bash dirty=$(git ls-files --modified) set +x if [[ $dirty ]]; then echo "=================================" echo "Files were not formatted properly" echo "$dirty" echo "=================================" exit 1 fiobs-text-slideshow-1.5.2/ci/ci_includes.cmd.in000066400000000000000000000001161415025745200212340ustar00rootroot00000000000000set PluginName=@CMAKE_PROJECT_NAME@ set PluginVersion=@CMAKE_PROJECT_VERSION@ obs-text-slideshow-1.5.2/ci/ci_includes.sh.in000066400000000000000000000002371415025745200211070ustar00rootroot00000000000000PLUGIN_NAME="@CMAKE_PROJECT_NAME@" PLUGIN_VERSION="@CMAKE_PROJECT_VERSION@" MACOS_BUNDLEID="@MACOS_BUNDLEID@" LINUX_MAINTAINER_EMAIL="@LINUX_MAINTAINER_EMAIL@"obs-text-slideshow-1.5.2/ci/linux/000077500000000000000000000000001415025745200170225ustar00rootroot00000000000000obs-text-slideshow-1.5.2/ci/linux/build-ubuntu.sh000077500000000000000000000001541415025745200220000ustar00rootroot00000000000000#!/bin/sh set -ex mkdir build && cd build cmake -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_UBUNTU=yes .. make -j4 obs-text-slideshow-1.5.2/ci/linux/install-dependencies-ubuntu.sh000077500000000000000000000006271415025745200250000ustar00rootroot00000000000000#!/bin/sh set -ex sudo add-apt-repository -y ppa:obsproject/obs-studio sudo apt-get -qq update sudo apt-get install -y \ libc-dev-bin \ libc6-dev git \ build-essential \ checkinstall \ cmake \ obs-studio \ qtbase5-dev # Dirty hack sudo wget -O /usr/include/obs/obs-frontend-api.h https://raw.githubusercontent.com/obsproject/obs-studio/25.0.0/UI/obs-frontend-api/obs-frontend-api.h sudo ldconfig obs-text-slideshow-1.5.2/ci/linux/package-ubuntu.sh000077500000000000000000000010651415025745200222760ustar00rootroot00000000000000#!/bin/bash set -e script_dir=$(dirname "$0") source "$script_dir/../ci_includes.generated.sh" export GIT_HASH=$(git rev-parse --short HEAD) cd ./build PAGER="cat" sudo checkinstall -y --type=debian --fstrans=no --nodoc \ --backup=no --deldoc=yes --install=no \ --pkgname="$PLUGIN_NAME" --pkgversion="$PLUGIN_VERSION" \ --pkglicense="GPLv2.0" --maintainer="$LINUX_MAINTAINER_EMAIL" \ --pkggroup="video" \ --requires="obs-studio \(\>= 27.0.0\), libqt5core5a \(\>= 5.14\), libqt5widgets5 \(\>= 5.14\)" \ --pakdir="../package" sudo chmod ao+r ../package/* obs-text-slideshow-1.5.2/ci/macos/000077500000000000000000000000001415025745200167655ustar00rootroot00000000000000obs-text-slideshow-1.5.2/ci/macos/build-macos.sh000077500000000000000000000012721415025745200215250ustar00rootroot00000000000000#!/bin/sh OSTYPE=$(uname) if [ "${OSTYPE}" != "Darwin" ]; then echo "[Error] macOS build script can be run on Darwin-type OS only." exit 1 fi HAS_CMAKE=$(type cmake 2>/dev/null) if [ "${HAS_CMAKE}" = "" ]; then echo "[Error] CMake not installed - please run 'install-dependencies-macos.sh' first." exit 1 fi echo "=> Building plugin for macOS." mkdir -p build && cd build cmake .. \ -DQTDIR="/tmp/obsdeps" \ -DLIBOBS_INCLUDE_DIR=../../obs-studio/libobs \ -DLIBOBS_LIB=../../obs-studio/libobs \ -DOBS_FRONTEND_LIB="$(pwd)/../../obs-studio/build/UI/obs-frontend-api/libobs-frontend-api.dylib" \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_INSTALL_PREFIX=/usr \ && make -j4 obs-text-slideshow-1.5.2/ci/macos/install-build-obs-macos.sh000077500000000000000000000026411415025745200237530ustar00rootroot00000000000000#!/bin/sh OSTYPE=$(uname) if [ "${OSTYPE}" != "Darwin" ]; then echo "[Error] macOS obs-studio build script can be run on Darwin-type OS only." exit 1 fi HAS_CMAKE=$(type cmake 2>/dev/null) HAS_GIT=$(type git 2>/dev/null) if [ "${HAS_CMAKE}" = "" ]; then echo "[Error] CMake not installed - please run 'install-dependencies-macos.sh' first." exit 1 fi if [ "${HAS_GIT}" = "" ]; then echo "[Error] Git not installed - please install Xcode developer tools or via Homebrew." exit 1 fi echo "=> Downloading and unpacking OBS dependencies" wget --quiet --retry-connrefused --waitretry=1 https://github.com/obsproject/obs-deps/releases/download/2021-03-25/macos-deps-x86_64-2021-03-25.tar.gz tar -xf ./macos-deps-x86_64-2021-03-25.tar.gz -C /tmp wget --quiet --retry-connrefused --waitretry=1 https://github.com/obsproject/obs-deps/releases/download/2021-03-25/macos-qt-5.15.2-x86_64-2021-03-25.tar.gz tar -xf ./macos-qt-5.15.2-x86_64-2021-03-25.tar.gz -C /tmp xattr -r -d com.apple.quarantine /tmp/obsdeps # Build obs-studio cd .. echo "=> Cloning obs-studio from GitHub.." git clone https://github.com/obsproject/obs-studio cd obs-studio OBSLatestTag=$(git describe --tags --abbrev=0) git checkout $OBSLatestTag mkdir build && cd build echo "=> Building obs-studio.." cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DDISABLE_PLUGINS=true -DENABLE_SCRIPTING=0 -DDepsPath="/tmp/obsdeps" -DQTDIR="/tmp/obsdeps" .. make -j4 obs-text-slideshow-1.5.2/ci/macos/install-dependencies-macos.sh000077500000000000000000000016351415025745200245230ustar00rootroot00000000000000#!/bin/sh OSTYPE=$(uname) if [ "${OSTYPE}" != "Darwin" ]; then echo "[Error] macOS install dependencies script can be run on Darwin-type OS only." exit 1 fi HAS_BREW=$(type brew 2>/dev/null) if [ "${HAS_BREW}" = "" ]; then echo "[Error] Please install Homebrew (https://www.brew.sh/) to build this plugin on macOS." exit 1 fi # OBS Studio deps echo "=> Updating Homebrew.." brew update >/dev/null echo "[=> Checking installed Homebrew formulas.." BREW_PACKAGES=$(brew list) BREW_DEPENDENCIES="jack speexdsp ccache swig mbedtls cmake wget" for DEPENDENCY in ${BREW_DEPENDENCIES}; do if echo "${BREW_PACKAGES}" | grep -q "^${DEPENDENCY}\$"; then echo "=> Upgrading OBS-Studio dependency '${DEPENDENCY}'.." brew upgrade ${DEPENDENCY} 2>/dev/null else echo "=> Installing OBS-Studio dependency '${DEPENDENCY}'.." brew install ${DEPENDENCY} 2>/dev/null fi done obs-text-slideshow-1.5.2/ci/macos/package-macos.sh000077500000000000000000000053371415025745200220270ustar00rootroot00000000000000#!/bin/bash set -e script_dir=$(dirname "$0") source "$script_dir/../ci_includes.generated.sh" OSTYPE=$(uname) if [ "${OSTYPE}" != "Darwin" ]; then echo "[Error] macOS package script can be run on Darwin-type OS only." exit 1 fi echo "=> Preparing package build" export QT_CELLAR_PREFIX="$(/usr/bin/find /usr/local/Cellar/qt -d 1 | sort -t '.' -k 1,1n -k 2,2n -k 3,3n | tail -n 1)" FILENAME_UNSIGNED="$PLUGIN_NAME-$PLUGIN_VERSION-Unsigned.pkg" FILENAME="$PLUGIN_NAME-$PLUGIN_VERSION.pkg" echo "=> Modifying $PLUGIN_NAME.so" install_name_tool \ -change /tmp/obsdeps/lib/QtWidgets.framework/Versions/5/QtWidgets \ @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets \ -change /tmp/obsdeps/lib/QtGui.framework/Versions/5/QtGui \ @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui \ -change /tmp/obsdeps/lib/QtCore.framework/Versions/5/QtCore \ @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore \ ./build/$PLUGIN_NAME.so # Check if replacement worked echo "=> Dependencies for $PLUGIN_NAME" otool -L ./build/$PLUGIN_NAME.so if [[ "$RELEASE_MODE" == "True" ]]; then echo "=> Signing plugin binary: $PLUGIN_NAME.so" codesign --sign "$CODE_SIGNING_IDENTITY" ./build/$PLUGIN_NAME.so else echo "=> Skipped plugin codesigning" fi echo "=> Actual package build" packagesbuild ./installer/installer-macOS.generated.pkgproj echo "=> Renaming $PLUGIN_NAME.pkg to $FILENAME" mv ./release/$PLUGIN_NAME.pkg ./release/$FILENAME_UNSIGNED if [[ "$RELEASE_MODE" == "True" ]]; then echo "=> Signing installer: $FILENAME" productsign \ --sign "$INSTALLER_SIGNING_IDENTITY" \ ./release/$FILENAME_UNSIGNED \ ./release/$FILENAME rm ./release/$FILENAME_UNSIGNED echo "=> Submitting installer $FILENAME for notarization" zip -r ./release/$FILENAME.zip ./release/$FILENAME UPLOAD_RESULT=$(xcrun altool \ --notarize-app \ --primary-bundle-id "$MACOS_BUNDLEID" \ --username "$AC_USERNAME" \ --password "$AC_PASSWORD" \ --asc-provider "$AC_PROVIDER_SHORTNAME" \ --file "./release/$FILENAME.zip") rm ./release/$FILENAME.zip REQUEST_UUID=$(echo $UPLOAD_RESULT | awk -F ' = ' '/RequestUUID/ {print $2}') echo "Request UUID: $REQUEST_UUID" echo "=> Wait for notarization result" # Pieces of code borrowed from rednoah/notarized-app while sleep 30 && date; do CHECK_RESULT=$(xcrun altool \ --notarization-info "$REQUEST_UUID" \ --username "$AC_USERNAME" \ --password "$AC_PASSWORD" \ --asc-provider "$AC_PROVIDER_SHORTNAME") echo $CHECK_RESULT if ! grep -q "Status: in progress" <<< "$CHECK_RESULT"; then echo "=> Staple ticket to installer: $FILENAME" xcrun stapler staple ./release/$FILENAME break fi done else echo "=> Skipped installer codesigning and notarization" fi obs-text-slideshow-1.5.2/ci/macos/qt.rb000066400000000000000000000124511415025745200177410ustar00rootroot00000000000000# Patches for Qt must be at the very least submitted to Qt's Gerrit codereview # rather than their bug-report Jira. The latter is rarely reviewed by Qt. class Qt < Formula desc "Cross-platform application and UI framework" homepage "https://www.qt.io/" url "https://download.qt.io/archive/qt/5.10/5.10.1/single/qt-everywhere-src-5.10.1.tar.xz" mirror "https://mirrorservice.org/sites/download.qt-project.org/official_releases/qt/5.10/5.10.1/single/qt-everywhere-src-5.10.1.tar.xz" sha256 "05ffba7b811b854ed558abf2be2ddbd3bb6ddd0b60ea4b5da75d277ac15e740a" head "https://code.qt.io/qt/qt5.git", :branch => "5.10.1", :shallow => false bottle do sha256 "8b4bad005596a5f8790150fe455db998ac2406f4e0f04140d6656205d844d266" => :high_sierra sha256 "9c488554935fb573554a4e36d36d3c81e47245b7fefc4b61edef894e67ba1740" => :sierra sha256 "c0407afba5951df6cc4c6f6c1c315972bd41c99cecb4e029919c4c15ab6f7bdc" => :el_capitan end keg_only "Qt 5 has CMake issues when linked" option "with-docs", "Build documentation" option "with-examples", "Build examples" deprecated_option "with-mysql" => "with-mysql-client" # OS X 10.7 Lion is still supported in Qt 5.5, but is no longer a reference # configuration and thus untested in practice. Builds on OS X 10.7 have been # reported to fail: . depends_on :macos => :mountain_lion depends_on "pkg-config" => :build depends_on :xcode => :build depends_on "mysql-client" => :optional depends_on "postgresql" => :optional # Restore `.pc` files for framework-based build of Qt 5 on OS X. This # partially reverts merged # between the 5.5.1 and 5.6.0 releases. (Remove this as soon as feasible!) # # Core formulae known to fail without this patch (as of 2016-10-15): # * gnuplot (with `--with-qt` option) # * mkvtoolnix (with `--with-qt` option, silent build failure) # * poppler (with `--with-qt` option) patch do url "https://raw.githubusercontent.com/Homebrew/formula-patches/e8fe6567/qt5/restore-pc-files.patch" sha256 "48ff18be2f4050de7288bddbae7f47e949512ac4bcd126c2f504be2ac701158b" end # Fix compile error on macOS 10.13 around QFixed: # https://github.com/Homebrew/homebrew-core/issues/27095 # https://bugreports.qt.io/browse/QTBUG-67545 patch do url "https://raw.githubusercontent.com/z00m1n/formula-patches/0de0e229/qt/QTBUG-67545.patch" sha256 "4a115097c7582c7dce4207f5500d13feb8c990eb8a05a43f41953985976ebe6c" end # Fix compile error on macOS 10.13 caused by qtlocation dependency # mapbox-gl-native using Boost 1.62.0 does not build with C++ 17: # https://github.com/Homebrew/homebrew-core/issues/27095 # https://bugreports.qt.io/browse/QTBUG-67810 patch do url "https://raw.githubusercontent.com/z00m1n/formula-patches/a1a1f0dd/qt/QTBUG-67810.patch" sha256 "8ee0bf71df1043f08ebae3aa35036be29c4d9ebff8a27e3b0411a6bd635e9382" end def install args = %W[ -verbose -prefix #{prefix} -release -opensource -confirm-license -system-zlib -qt-libpng -qt-libjpeg -qt-freetype -qt-pcre -nomake tests -no-rpath -pkg-config -dbus-runtime -no-assimp ] args << "-nomake" << "examples" if build.without? "examples" if build.with? "mysql-client" args << "-plugin-sql-mysql" (buildpath/"brew_shim/mysql_config").write <<~EOS #!/bin/sh if [ x"$1" = x"--libs" ]; then mysql_config --libs | sed "s/-lssl -lcrypto//" else exec mysql_config "$@" fi EOS chmod 0755, "brew_shim/mysql_config" args << "-mysql_config" << buildpath/"brew_shim/mysql_config" end args << "-plugin-sql-psql" if build.with? "postgresql" system "./configure", *args system "make" ENV.deparallelize system "make", "install" if build.with? "docs" system "make", "docs" system "make", "install_docs" end # Some config scripts will only find Qt in a "Frameworks" folder frameworks.install_symlink Dir["#{lib}/*.framework"] # The pkg-config files installed suggest that headers can be found in the # `include` directory. Make this so by creating symlinks from `include` to # the Frameworks' Headers folders. Pathname.glob("#{lib}/*.framework/Headers") do |path| include.install_symlink path => path.parent.basename(".framework") end # Move `*.app` bundles into `libexec` to expose them to `brew linkapps` and # because we don't like having them in `bin`. # (Note: This move breaks invocation of Assistant via the Help menu # of both Designer and Linguist as that relies on Assistant being in `bin`.) libexec.mkpath Pathname.glob("#{bin}/*.app") { |app| mv app, libexec } end def caveats; <<~EOS We agreed to the Qt opensource license for you. If this is unacceptable you should uninstall. EOS end test do (testpath/"hello.pro").write <<~EOS QT += core QT -= gui TARGET = hello CONFIG += console CONFIG -= app_bundle TEMPLATE = app SOURCES += main.cpp EOS (testpath/"main.cpp").write <<~EOS #include #include int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug() << "Hello World!"; return 0; } EOS system bin/"qmake", testpath/"hello.pro" system "make" assert_predicate testpath/"hello", :exist? assert_predicate testpath/"main.o", :exist? system "./hello" end end obs-text-slideshow-1.5.2/ci/windows/000077500000000000000000000000001415025745200173555ustar00rootroot00000000000000obs-text-slideshow-1.5.2/ci/windows/download-obs-deps.cmd000066400000000000000000000004031415025745200233600ustar00rootroot00000000000000if not exist %DepsBasePath% ( curl -o %DepsBasePath%.zip -kLO https://obsproject.com/downloads/dependencies2017.zip -f --retry 5 -C - 7z x %DepsBasePath%.zip -o%DepsBasePath% ) else ( echo "OBS dependencies are already there. Download skipped." ) obs-text-slideshow-1.5.2/ci/windows/install-qt-win.cmd000066400000000000000000000003571415025745200227320ustar00rootroot00000000000000if not exist %QtBaseDir% ( curl -kLO https://cdn-fastly.obsproject.com/downloads/Qt_5.10.1.7z -f --retry 5 -z Qt_5.10.1.7z 7z x Qt_5.10.1.7z -o%QtBaseDir% ) else ( echo "Qt is already installed. Download skipped." ) dir %QtBaseDir% obs-text-slideshow-1.5.2/ci/windows/package-windows.cmd000066400000000000000000000006201415025745200231230ustar00rootroot00000000000000call "%~dp0..\ci_includes.generated.cmd" mkdir package cd package git rev-parse --short HEAD > package-version.txt set /p PackageVersion= "%OBSPath%\obs-studio-latest-tag.txt" set /p OBSLatestTag=<"%OBSPath%\obs-studio-latest-tag.txt" ) REM Prepare OBS Studio builds echo Running CMake... cd /D %OBSPath% echo git checkout %OBSLatestTag% git checkout %OBSLatestTag% echo: if not exist build32 mkdir build32 if not exist build64 mkdir build64 echo Running cmake for obs-studio %OBSLatestTag% 32-bit... cd build32 cmake -G "Visual Studio 16 2019" -A Win32 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR32%" -DDepsPath="%DepsPath32%" -DBUILD_CAPTIONS=true -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true .. echo: echo: echo Running cmake for obs-studio %OBSLatestTag% 64-bit... cd ..\build64 cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR64%" -DDepsPath="%DepsPath64%" -DBUILD_CAPTIONS=true -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true .. echo: echo: dir "%OBSPath%\libobs" obs-text-slideshow-1.5.2/ci/windows/prepare-windows.cmd000066400000000000000000000017051415025745200231730ustar00rootroot00000000000000mkdir build32 mkdir build64 cd build32 cmake -G "Visual Studio 16 2019" -A Win32 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR32%" -DLibObs_DIR="%OBSPath%\build32\libobs" -DLIBOBS_INCLUDE_DIR="%OBSPath%\libobs" -DLIBOBS_LIB="%OBSPath%\build32\libobs\%build_config%\obs.lib" -DOBS_FRONTEND_LIB="%OBSPath%\build32\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib" .. cd ..\build64 cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR64%" -DLibObs_DIR="%OBSPath%\build64\libobs" -DLIBOBS_INCLUDE_DIR="%OBSPath%\libobs" -DLIBOBS_LIB="%OBSPath%\build64\libobs\%build_config%\obs.lib" -DOBS_FRONTEND_LIB="%OBSPath%\build64\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib" .. REM Import the generated includes to get the plugin's name call "%~dp0..\ci_includes.generated.cmd" REM Rename the solution files to something CI can pick up cd .. ren "build32\%PluginName%.sln" "main.sln" ren "build64\%PluginName%.sln" "main.sln" obs-text-slideshow-1.5.2/data/000077500000000000000000000000001415025745200162015ustar00rootroot00000000000000obs-text-slideshow-1.5.2/data/locale/000077500000000000000000000000001415025745200174405ustar00rootroot00000000000000obs-text-slideshow-1.5.2/data/locale/de-DE.ini000066400000000000000000000057721415025745200210320ustar00rootroot00000000000000TextFreetype2Slideshow="Text Slide Show (Free Type 2)" TextGdiplusSlideshow="Text Slide Show (GDI+)" SlideShow.TransitionSpeed="Übergangs Geschwindigkeit (Millisekunden)" SlideShow.SlideTime="Zeit zwischen Übergängen (Millisekunden)" SlideShow.Texts="Inhalt (die anzuzeigende Texte)" SlideShow.CustomSize="Begrenzungsgröße/Seitenverhältnis" SlideShow.CustomSize.Auto="Automatisch" SlideShow.Randomize="Zufällig Wiedergeben" SlideShow.Loop="Schleife" SlideShow.Transition="Übergang" SlideShow.Transition.Cut="Schnitt" SlideShow.Transition.Fade="Überblenden" SlideShow.Transition.Swipe="Wischen" SlideShow.Transition.Slide="Rutschen" SlideShow.PlaybackBehavior="Verhalten bei Sichtbarkeit" SlideShow.PlaybackBehavior.StopRestart="Stoppen wenn nicht sichbar, neustarten wenn sichtbar" SlideShow.PlaybackBehavior.PauseUnpause="Pausieren wenn nicht sichbar, Fortfaren wenn sichtbar" SlideShow.PlaybackBehavior.AlwaysPlay="Immer Abspielen, auch wenn nicht sichtbar" SlideShow.SlideMode="Übergangs Modus" SlideShow.SlideMode.Auto="Automatisch" SlideShow.SlideMode.Manual="Manuell (Nutze Hotkeys zum Wechseln)" SlideShow.PlayPause="Play/Pause" SlideShow.Restart="Restart" SlideShow.Stop="Stop" SlideShow.NextSlide="Nexter Text" SlideShow.PreviousSlide="Voriger Text" SlideShow.HideWhenDone="Verstecken wenn Beendet" SlideShow.ReadFromSingleFile="Aus einzelner Datei lesen" SlideShow.UseCustomDelim="Benutzerdifferenziertes Limit verwenden" SlideShow.CustomDelim="Benutzerdifferenziertes Limit" SlideShow.TextFile="Text Datei (UTF-8)" SlideShow.Filter.TextFiles="Text Dateien" SlideShow.Filter.AllFiles="Alle Dateien" Slideshow.ReadFromMultipleFiles="Aus mehreren Dateien lesen" Slideshow.Files="Text Dateien (UTF-8)" Text.Font="Schriftart" Text.Text="Text" Text.Color="Farbe" Text.Opacity="Transparenz" Text.Gradient="Gradient" Text.Gradient.Color="Gradient Farbe" Text.Gradient.Opacity="Gradient Transparenz" Text.Gradient.Direction="Gradient Richtung" Text.BkColor="Hintergrundfarbe" Text.BkOpacity="Hintergrund Transparenz" Text.Alignment="Ausrichtung" Text.Alignment.Left="Links" Text.Alignment.Center="Zentriert" Text.Alignment.Right="Rechts" Text.Vertical="Vertikal" Text.VerticalAlignment="Vertikale Ausrichtung" Text.VerticalAlignment.Top="Oben" Text.VerticalAlignment.Bottom="Unten" Text.Outline="Umrandung" Text.Outline.Size="Größe Umrandung" Text.Outline.Color="Farbe Umrandung" Text.Outline.Opacity="Tranzparenz Umrandung" Text.ChatlogMode="Chatlog Modus" Text.ChatlogMode.Lines="Chatlog Zeilen Limit" Text.UseCustomExtents="Use Custom Text Extents" Text.UseCustomExtents.Wrap="Wrap" Text.Width="Breite" Text.Height="Höhe" Text.Transform="Text Transform" Text.Transform.None="None" Text.Transform.Uppercase="Großschrift" Text.Transform.Lowercase="Kleinschrift" Text.Transform.Startcase="Start Schrift" Text.Antialiasing="Antialiasing" Text.ChatLogLines="Chat Log Zeilen" Text.Color1="Farbe 1" Text.Color2="Farbe 2" Text.Outline="Umrandung" Text.DropShadow="Schatten" Text.CustomWidth="benutzerdifferenzierte Textbreite" Text.WordWrap="Word Wrap" obs-text-slideshow-1.5.2/data/locale/en-US.ini000066400000000000000000000054421415025745200210750ustar00rootroot00000000000000TextFreetype2Slideshow="Text Slide Show (Free Type 2)" TextGdiplusSlideshow="Text Slide Show (GDI+)" SlideShow.TransitionSpeed="Transition Speed (milliseconds)" SlideShow.SlideTime="Time Between Slides (milliseconds)" SlideShow.Texts="Slide Texts" SlideShow.CustomSize="Bounding Size/Aspect Ratio" SlideShow.CustomSize.Auto="Automatic" SlideShow.Randomize="Randomize Playback" SlideShow.Loop="Loop" SlideShow.Transition="Transition" SlideShow.Transition.Cut="Cut" SlideShow.Transition.Fade="Fade" SlideShow.Transition.Swipe="Swipe" SlideShow.Transition.Slide="Slide" SlideShow.PlaybackBehavior="Visibility Behavior" SlideShow.PlaybackBehavior.StopRestart="Stop when not visible, restart when visible" SlideShow.PlaybackBehavior.PauseUnpause="Pause when not visible, unpause when visible" SlideShow.PlaybackBehavior.AlwaysPlay="Always play even when not visible" SlideShow.SlideMode="Slide Mode" SlideShow.SlideMode.Auto="Automatic" SlideShow.SlideMode.Manual="Manual (Use hotkeys to control slideshow)" SlideShow.PlayPause="Play/Pause" SlideShow.Restart="Restart" SlideShow.Stop="Stop" SlideShow.NextSlide="Next Slide" SlideShow.PreviousSlide="Previous Slide" SlideShow.HideWhenDone="Hide when slideshow is done" SlideShow.ReadFromSingleFile="Read from single file" SlideShow.UseCustomDelim="Use Custom Delimiter" SlideShow.CustomDelim="Custom Delimiter" SlideShow.TextFile="Text File (UTF-8)" SlideShow.Filter.TextFiles="Text Files" SlideShow.Filter.AllFiles="All Files" Slideshow.ReadFromMultipleFiles="Read from multiple files" Slideshow.Files="Text Files (UTF-8)" Text.Font="Font" Text.Text="Text" Text.Color="Color" Text.Opacity="Opacity" Text.Gradient="Gradient" Text.Gradient.Color="Gradient Color" Text.Gradient.Opacity="Gradient Opacity" Text.Gradient.Direction="Gradient Direction" Text.BkColor="Background Color" Text.BkOpacity="Background Opacity" Text.Alignment="Alignment" Text.Alignment.Left="Left" Text.Alignment.Center="Center" Text.Alignment.Right="Right" Text.Vertical="Vertical" Text.VerticalAlignment="Vertical Alignment" Text.VerticalAlignment.Top="Top" Text.VerticalAlignment.Bottom="Bottom" Text.Outline="Outline" Text.Outline.Size="Outline Size" Text.Outline.Color="Outline Color" Text.Outline.Opacity="Outline Opacity" Text.ChatlogMode="Chatlog Mode" Text.ChatlogMode.Lines="Chatlog Line Limit" Text.UseCustomExtents="Use Custom Text Extents" Text.UseCustomExtents.Wrap="Wrap" Text.Width="Width" Text.Height="Height" Text.Transform="Text Transform" Text.Transform.None="None" Text.Transform.Uppercase="Uppercase" Text.Transform.Lowercase="Lowercase" Text.Transform.Startcase="Start Case" Text.Antialiasing="Enable Antialiasing" Text.ChatLogLines="Chat log lines" Text.Color1="Color 1" Text.Color2="Color 2" Text.Outline="Outline" Text.DropShadow="Drop Shadow" Text.CustomWidth="Custom text width" Text.WordWrap="Word Wrap"obs-text-slideshow-1.5.2/data/locale/it_IT.ini000066400000000000000000000063611415025745200211570ustar00rootroot00000000000000TextFreetype2Slideshow="Text Slide Show (Free Type 2)" TextGdiplusSlideshow="Text Slide Show (GDI+)" SlideShow.TransitionSpeed="Velocità di transizione (millisecondi)" SlideShow.SlideTime="Tempo fra le diapositive (millisecondi)" SlideShow.Texts="Contenuto (testo da visualizzare)" SlideShow.CustomSize="Dimensione area/Proporzioni" SlideShow.CustomSize.Auto="Automatico" SlideShow.Randomize="Randomizza Riproduzione" SlideShow.Loop="Ripeti" SlideShow.Transition="Transizione" SlideShow.Transition.Cut="Taglia" SlideShow.Transition.Fade="Dissolvenza" SlideShow.Transition.Swipe="Scorri" SlideShow.Transition.Slide="Diapositiva" SlideShow.PlaybackBehavior="Comportamento della Riproduzione" SlideShow.PlaybackBehavior.StopRestart="Ferma quando non visibile, riprendi quando visibile" SlideShow.PlaybackBehavior.PauseUnpause="Pausa quando non visibile, riprendi quandi visibile" SlideShow.PlaybackBehavior.AlwaysPlay="Riproduci sempre quando non visibile" SlideShow.SlideMode="Modalità diapositiva" SlideShow.SlideMode.Auto="Automatico" SlideShow.SlideMode.Manual="Manuale (Usa le scorciatoie da tasiera per controllare la riproduzione)" SlideShow.PlayPause="Riproduci/Metti in Pausa" SlideShow.Restart="Riavvia" SlideShow.Stop="Ferma" SlideShow.NextSlide="Prossima Diapositiva" SlideShow.PreviousSlide="Diapositiva Precedente" SlideShow.HideWhenDone="Nascondi Quando la Riproduzione è Conclusa" SlideShow.ReadFromSingleFile="Leggi da Singolo File" SlideShow.UseCustomDelim="Utilizza un Delimitatore Personalizzato" SlideShow.CustomDelim="Delimitatore Personalizzato" SlideShow.TextFile="File Testuale (UTF-8)" SlideShow.Filter.TextFiles="File Testuali" SlideShow.Filter.AllFiles="Tutti i File" Slideshow.ReadFromMultipleFiles="Leggi da File Multipli" Slideshow.Files="File Testuali (UTF-8)" Text.Font="Font" Text.Text="Testo" Text.Color="Colore" Text.Opacity="Opacità" Text.Gradient="Gradiente" Text.Gradient.Color="Colore del Gradiente" Text.Gradient.Opacity="Opacità del Gradiente" Text.Gradient.Direction="Direzione del Gradiente" Text.BkColor="Colore di Sfondo" Text.BkOpacity="Opacità dello Sfondo" Text.Alignment="Allineamento" Text.Alignment.Left="Sinistra" Text.Alignment.Center="Centro" Text.Alignment.Right="Destra" Text.Vertical="Verticale" Text.VerticalAlignment="Allineamento Verticale" Text.VerticalAlignment.Top="In Alto" Text.VerticalAlignment.Bottom="In Basso" Text.Outline="Contorno" Text.Outline.Size="Spessore Contorno" Text.Outline.Color="Colore Contorno" Text.Outline.Opacity="Opacità Contorno" Text.ChatlogMode="Modalità Registro di Chat" Text.ChatlogMode.Lines="Limite di Linee del Registro di Chat" Text.UseCustomExtents="Usa Estensioni di Testo Personalizzate" Text.UseCustomExtents.Wrap="A Capo" Text.Width="Larghezza" Text.Height="Altezza" Text.Transform="Trasformazione del Testo" Text.Transform.None="Nessuna" Text.Transform.Uppercase="Maiuscolo" Text.Transform.Lowercase="Minuscolo" Text.Transform.Startcase="Prima Lettera Maiuscola" Text.Antialiasing="Abilita Antialiasing" Text.ChatLogLines="Linee del Registro di Chat" Text.Color1="Colore 1" Text.Color2="Colore 2" Text.Outline="Contorno" Text.DropShadow="Ombreggiatura" Text.CustomWidth="Larghezza Testo Personalizzata" Text.WordWrap="A Capo"obs-text-slideshow-1.5.2/external/000077500000000000000000000000001415025745200171125ustar00rootroot00000000000000obs-text-slideshow-1.5.2/external/FindLibObs.cmake000066400000000000000000000057471415025745200221040ustar00rootroot00000000000000# This module can be copied and used by external plugins for OBS # # Once done these will be defined: # # LIBOBS_FOUND # LIBOBS_INCLUDE_DIRS # LIBOBS_LIBRARIES find_package(PkgConfig QUIET) if (PKG_CONFIG_FOUND) pkg_check_modules(_OBS QUIET obs libobs) endif() if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(_lib_suffix 64) else() set(_lib_suffix 32) endif() if(DEFINED CMAKE_BUILD_TYPE) if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(_build_type_base "debug") else() set(_build_type_base "release") endif() endif() find_path(LIBOBS_INCLUDE_DIR NAMES obs.h HINTS ENV obsPath${_lib_suffix} ENV obsPath ${obsPath} PATHS /usr/include /usr/local/include /opt/local/include /sw/include PATH_SUFFIXES libobs ) function(find_obs_lib base_name repo_build_path lib_name) string(TOUPPER "${base_name}" base_name_u) if(DEFINED _build_type_base) set(_build_type_${repo_build_path} "${_build_type_base}/${repo_build_path}") set(_build_type_${repo_build_path}${_lib_suffix} "${_build_type_base}${_lib_suffix}/${repo_build_path}") endif() find_library(${base_name_u}_LIB NAMES ${_${base_name_u}_LIBRARIES} ${lib_name} lib${lib_name} HINTS ENV obsPath${_lib_suffix} ENV obsPath ${obsPath} ${_${base_name_u}_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib PATH_SUFFIXES lib${_lib_suffix} lib libs${_lib_suffix} libs bin${_lib_suffix} bin ../lib${_lib_suffix} ../lib ../libs${_lib_suffix} ../libs ../bin${_lib_suffix} ../bin # base repo non-msvc-specific search paths ${_build_type_${repo_build_path}} ${_build_type_${repo_build_path}${_lib_suffix}} build/${repo_build_path} build${_lib_suffix}/${repo_build_path} # base repo msvc-specific search paths on windows build${_lib_suffix}/${repo_build_path}/Debug build${_lib_suffix}/${repo_build_path}/RelWithDebInfo build/${repo_build_path}/Debug build/${repo_build_path}/RelWithDebInfo ) endfunction() find_obs_lib(LIBOBS libobs obs) if(MSVC) find_obs_lib(W32_PTHREADS deps/w32-pthreads w32-pthreads) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Libobs DEFAULT_MSG LIBOBS_LIB LIBOBS_INCLUDE_DIR) mark_as_advanced(LIBOBS_INCLUDE_DIR LIBOBS_LIB) if(LIBOBS_FOUND) if(MSVC) if (NOT DEFINED W32_PTHREADS_LIB) message(FATAL_ERROR "Could not find the w32-pthreads library" ) endif() set(W32_PTHREADS_INCLUDE_DIR ${LIBOBS_INCLUDE_DIR}/../deps/w32-pthreads) endif() set(LIBOBS_INCLUDE_DIRS ${LIBOBS_INCLUDE_DIR} ${W32_PTHREADS_INCLUDE_DIR}) set(LIBOBS_LIBRARIES ${LIBOBS_LIB} ${W32_PTHREADS_LIB}) include(${LIBOBS_INCLUDE_DIR}/../cmake/external/ObsPluginHelpers.cmake) # allows external plugins to easily use/share common dependencies that are often included with libobs (such as FFmpeg) if(NOT DEFINED INCLUDED_LIBOBS_CMAKE_MODULES) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${LIBOBS_INCLUDE_DIR}/../cmake/Modules/") set(INCLUDED_LIBOBS_CMAKE_MODULES true) endif() else() message(FATAL_ERROR "Could not find the libobs library" ) endif() obs-text-slideshow-1.5.2/formatcode.sh000077500000000000000000000016061415025745200177550ustar00rootroot00000000000000#!/usr/bin/env bash # Original source https://github.com/Project-OSRM/osrm-backend/blob/master/scripts/format.sh set +x set -o errexit set -o pipefail set -o nounset # Runs the Clang Formatter in parallel on the code base. # Return codes: # - 1 there are files to be formatted # - 0 everything looks fine # Get CPU count OS=$(uname) NPROC=1 if [[ $OS = "Linux" || $OS = "Darwin" ]] ; then NPROC=$(getconf _NPROCESSORS_ONLN) fi # Discover clang-format if type clang-format-10 2> /dev/null ; then CLANG_FORMAT=clang-format-10 elif type clang-format-8 2> /dev/null ; then CLANG_FORMAT=clang-format-8 else CLANG_FORMAT=clang-format fi find . -type d \( -path ./deps \ -o -path ./build \) -prune -type f -o -name '*.h' -or -name '*.hpp' -or -name '*.m' -or -name '*.mm' -or -name '*.c' -or -name '*.cpp' \ | xargs -L100 -P${NPROC} ${CLANG_FORMAT} -i -style=file -fallback-style=none obs-text-slideshow-1.5.2/installer/000077500000000000000000000000001415025745200172655ustar00rootroot00000000000000obs-text-slideshow-1.5.2/installer/installer-Windows.iss.in000066400000000000000000000044261415025745200240450ustar00rootroot00000000000000#define MyAppName "@CMAKE_PROJECT_NAME@" #define MyAppVersion "@CMAKE_PROJECT_VERSION@" #define MyAppPublisher "@PLUGIN_AUTHOR@" #define MyAppURL "https://github.com/jbwong05/obs-text-slideshow" [Setup] ; NOTE: The value of AppId uniquely identifies this application. ; Do not use the same AppId value in installers for other applications. ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) AppId={{CD703FE5-1F2C-4837-BD3D-DD840D83C3E3} AppName={#MyAppName} AppVersion={#MyAppVersion} AppPublisher={#MyAppPublisher} AppPublisherURL={#MyAppURL} AppSupportURL={#MyAppURL} AppUpdatesURL={#MyAppURL} DefaultDirName={code:GetDirName} DefaultGroupName={#MyAppName} OutputBaseFilename={#MyAppName}-{#MyAppVersion}-Windows-Installer Compression=lzma SolidCompression=yes [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" [Files] Source: "..\release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs Source: "..\LICENSE"; Flags: dontcopy ; NOTE: Don't use "Flags: ignoreversion" on any shared system files [Icons] Name: "{group}\{cm:ProgramOnTheWeb,{#MyAppName}}"; Filename: "{#MyAppURL}" Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}" [Code] procedure InitializeWizard(); var GPLText: AnsiString; Page: TOutputMsgMemoWizardPage; begin ExtractTemporaryFile('LICENSE'); LoadStringFromFile(ExpandConstant('{tmp}\LICENSE'), GPLText); Page := CreateOutputMsgMemoPage(wpWelcome, 'License Information', 'Please review the license terms before installing {#MyAppName}', 'Press Page Down to see the rest of the agreement. Once you are aware of your rights, click Next to continue.', String(GPLText) ); end; // credit where it's due : // following function come from https://github.com/Xaymar/obs-studio_amf-encoder-plugin/blob/master/%23Resources/Installer.in.iss#L45 function GetDirName(Value: string): string; var InstallPath: string; begin // initialize default path, which will be returned when the following registry // key queries fail due to missing keys or for some different reason Result := '{pf}\obs-studio'; // query the first registry value; if this succeeds, return the obtained value if RegQueryStringValue(HKLM32, 'SOFTWARE\OBS Studio', '', InstallPath) then Result := InstallPath end; obs-text-slideshow-1.5.2/installer/installer-macOS.pkgproj.in000066400000000000000000000450761415025745200243010ustar00rootroot00000000000000 PROJECT PACKAGE_FILES DEFAULT_INSTALL_LOCATION / HIERARCHY CHILDREN CHILDREN CHILDREN CHILDREN CHILDREN CHILDREN CHILDREN CHILDREN GID 80 PATH ../build/@CMAKE_PROJECT_NAME@.so PATH_TYPE 1 PERMISSIONS 493 TYPE 3 UID 0 GID 80 PATH bin PATH_TYPE 0 PERMISSIONS 493 TYPE 2 UID 0 CHILDREN GID 80 PATH ../data PATH_TYPE 1 PERMISSIONS 493 TYPE 3 UID 0 GID 80 PATH @CMAKE_PROJECT_NAME@ PATH_TYPE 0 PERMISSIONS 493 TYPE 2 UID 0 GID 80 PATH plugins PATH_TYPE 0 PERMISSIONS 493 TYPE 2 UID 0 GID 80 PATH obs-studio PATH_TYPE 0 PERMISSIONS 493 TYPE 2 UID 0 GID 80 PATH Application Support PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Automator PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Documentation PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Extensions PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Filesystems PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Frameworks PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Input Methods PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Internet Plug-Ins PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH LaunchAgents PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH LaunchDaemons PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH PreferencePanes PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Preferences PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 80 PATH Printers PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH PrivilegedHelperTools PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH QuickLook PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH QuickTime PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Screen Savers PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Scripts PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Services PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Widgets PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 GID 0 PATH Library PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN CHILDREN GID 0 PATH Shared PATH_TYPE 0 PERMISSIONS 1023 TYPE 1 UID 0 GID 80 PATH Users PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 GID 0 PATH / PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 PAYLOAD_TYPE 0 VERSION 4 PACKAGE_SCRIPTS RESOURCES PACKAGE_SETTINGS AUTHENTICATION 1 CONCLUSION_ACTION 0 IDENTIFIER @MACOS_BUNDLEID@ OVERWRITE_PERMISSIONS VERSION @CMAKE_PROJECT_VERSION@ PROJECT_COMMENTS NOTES PCFET0NUWVBFIGh0bWwgUFVCTElDICItLy9XM0MvL0RURCBIVE1M IDQuMDEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvVFIvaHRtbDQv c3RyaWN0LmR0ZCI+CjxodG1sPgo8aGVhZD4KPG1ldGEgaHR0cC1l cXVpdj0iQ29udGVudC1UeXBlIiBjb250ZW50PSJ0ZXh0L2h0bWw7 IGNoYXJzZXQ9VVRGLTgiPgo8bWV0YSBodHRwLWVxdWl2PSJDb250 ZW50LVN0eWxlLVR5cGUiIGNvbnRlbnQ9InRleHQvY3NzIj4KPHRp dGxlPjwvdGl0bGU+CjxtZXRhIG5hbWU9IkdlbmVyYXRvciIgY29u dGVudD0iQ29jb2EgSFRNTCBXcml0ZXIiPgo8bWV0YSBuYW1lPSJD b2NvYVZlcnNpb24iIGNvbnRlbnQ9IjE0MDQuMTMiPgo8c3R5bGUg dHlwZT0idGV4dC9jc3MiPgo8L3N0eWxlPgo8L2hlYWQ+Cjxib2R5 Pgo8L2JvZHk+CjwvaHRtbD4K PROJECT_SETTINGS BUILD_PATH PATH ../release PATH_TYPE 1 EXCLUDED_FILES PATTERNS_ARRAY REGULAR_EXPRESSION STRING .DS_Store TYPE 0 PROTECTED PROXY_NAME Remove .DS_Store files PROXY_TOOLTIP Remove ".DS_Store" files created by the Finder. STATE PATTERNS_ARRAY REGULAR_EXPRESSION STRING .pbdevelopment TYPE 0 PROTECTED PROXY_NAME Remove .pbdevelopment files PROXY_TOOLTIP Remove ".pbdevelopment" files created by ProjectBuilder or Xcode. STATE PATTERNS_ARRAY REGULAR_EXPRESSION STRING CVS TYPE 1 REGULAR_EXPRESSION STRING .cvsignore TYPE 0 REGULAR_EXPRESSION STRING .cvspass TYPE 0 REGULAR_EXPRESSION STRING .svn TYPE 1 REGULAR_EXPRESSION STRING .git TYPE 1 REGULAR_EXPRESSION STRING .gitignore TYPE 0 PROTECTED PROXY_NAME Remove SCM metadata PROXY_TOOLTIP Remove helper files and folders used by the CVS, SVN or Git Source Code Management systems. STATE PATTERNS_ARRAY REGULAR_EXPRESSION STRING classes.nib TYPE 0 REGULAR_EXPRESSION STRING designable.db TYPE 0 REGULAR_EXPRESSION STRING info.nib TYPE 0 PROTECTED PROXY_NAME Optimize nib files PROXY_TOOLTIP Remove "classes.nib", "info.nib" and "designable.nib" files within .nib bundles. STATE PATTERNS_ARRAY REGULAR_EXPRESSION STRING Resources Disabled TYPE 1 PROTECTED PROXY_NAME Remove Resources Disabled folders PROXY_TOOLTIP Remove "Resources Disabled" folders. STATE SEPARATOR NAME @CMAKE_PROJECT_NAME@ TYPE 1 VERSION 2 obs-text-slideshow-1.5.2/src/000077500000000000000000000000001415025745200160575ustar00rootroot00000000000000obs-text-slideshow-1.5.2/src/files.cpp000066400000000000000000000125671415025745200177000ustar00rootroot00000000000000#include "files.h" #include #include "plugin-macros.generated.h" #define CHUNK_LEN 256 static void remove_starting_new_line(char **text_ptr) { char *text = *text_ptr; size_t len = strlen(text); if (len >= 2 && text[0] == '\r' && text[1] == '\n') { *text_ptr += 2; } else if (len >= 1 && text[0] == '\n') { (*text_ptr)++; } } static void remove_ending_new_line(char *text) { size_t len = strlen(text); if (len >= 2 && text[len - 2] == '\r' && text[len - 1] == '\n') { text[len - 2] = 0; text[len - 1] = 0; } else if (len >= 1 && text[len - 1] == '\n') { text[len - 1] = 0; } } static void remove_new_lines(size_t start, vector &texts) { // Remove trailing new lines for (size_t i = start; i < texts.size(); i++) { remove_ending_new_line(texts[i]); } } static void load_text_from_file(vector &texts, const char *file_path, const char *delim) { FILE *file = os_fopen(file_path, "rb"); /* should check the result */ if (file == NULL) { blog(LOG_WARNING, "Failed to open file %s", file_path); return; } uint16_t header = 0; size_t num_read = fread(&header, 2, 1, file); if (num_read == 1 && (header == 0xFEFF || header == 0xFFFE)) { blog(LOG_WARNING, "UTF-16 not supported for file %s", file_path); fclose(file); return; } fseek(file, 0, SEEK_SET); char chunk[CHUNK_LEN]; memset(chunk, 0, CHUNK_LEN); bool add_new_line = true; size_t read = 0; while ((read = fread(chunk, sizeof(char), CHUNK_LEN - 1, file))) { bool end_in_delim = chunk[read - 1] == *delim; add_new_line = add_new_line || chunk[0] == *delim; chunk[read] = 0; #ifdef _WIN32 char *next_token = NULL; char *token = strtok_s(chunk, delim, &next_token); #else char *token = strtok(chunk, delim); #endif while (token) { remove_starting_new_line(&token); remove_ending_new_line(token); size_t token_len = strlen(token); if (add_new_line) { // Need to add new string char *curr_text = (char *)bzalloc(token_len + 1); if (curr_text == NULL) { fclose(file); return; } #ifdef _WIN32 strncpy_s(curr_text, token_len + 1, token, token_len); #else strncpy(curr_text, token, token_len); #endif texts.push_back(curr_text); } else { // Need to append to existing string size_t curr_index = texts.size() - 1; size_t existing_len = strlen(texts[curr_index]); char *new_ptr = (char *)brealloc( (void *)texts[curr_index], existing_len + token_len + 1); if (new_ptr == NULL) { fclose(file); return; } #ifdef _WIN32 strncpy_s(new_ptr + existing_len, token_len + 1, token, token_len); #else strncpy(new_ptr + existing_len, token, token_len); #endif new_ptr[existing_len + token_len] = 0; texts[curr_index] = new_ptr; add_new_line = true; } #ifdef _WIN32 token = strtok_s(NULL, delim, &next_token); #else token = strtok(NULL, delim); #endif } add_new_line = end_in_delim; } remove_new_lines(texts.size() - 1, texts); fclose(file); } static void load_text_from_file(vector &texts, const char *file_path) { FILE *file = os_fopen(file_path, "rb"); /* should check the result */ if (file == NULL) { blog(LOG_WARNING, "Failed to open file %s", file_path); return; } uint16_t header = 0; size_t num_read = fread(&header, 2, 1, file); if (num_read == 1 && (header == 0xFEFF || header == 0xFFFE)) { blog(LOG_WARNING, "UTF-16 not supported for file %s", file_path); fclose(file); return; } fseek(file, 0, SEEK_SET); char line[CHUNK_LEN]; memset(line, 0, CHUNK_LEN); bool add_new_line = true; bool prev_new_line = false; while (fgets(line, sizeof(line), file)) { size_t curr_len = strlen(line); if ((curr_len == 2 && line[curr_len - 2] == '\r' && line[curr_len - 1] == '\n') || (curr_len == 1 && line[curr_len - 1] == '\n')) { add_new_line = true; if (!prev_new_line) { prev_new_line = true; continue; } } if (add_new_line) { // Need to add new string char *curr_text = (char *)bzalloc(curr_len + 1); if (curr_text == NULL) { fclose(file); return; } #ifdef _WIN32 strncpy_s(curr_text, curr_len + 1, line, curr_len); #else strncpy(curr_text, line, curr_len); #endif texts.push_back(curr_text); add_new_line = false; prev_new_line = false; } else { // Need to append to existing string size_t curr_index = texts.size() - 1; size_t existing_len = strlen(texts[curr_index]); char *new_ptr = (char *)brealloc((void *)texts[curr_index], existing_len + curr_len + 1); if (new_ptr == NULL) { fclose(file); return; } #ifdef _WIN32 strncpy_s(new_ptr + existing_len, curr_len + 1, line, curr_len); #else strncpy(new_ptr + existing_len, line, curr_len); #endif new_ptr[existing_len + curr_len] = 0; texts[curr_index] = new_ptr; } } remove_new_lines(0, texts); fclose(file); } void read_file(struct text_slideshow *text_ss, obs_data_t *settings, vector &texts) { const char *file_path = text_ss->file.c_str(); if (!file_path || !*file_path || !os_file_exists(file_path)) { blog(LOG_WARNING, "Failed to open %s for " "reading", file_path); } else { if (!text_ss->file.empty()) { if (text_ss->custom_delim) { load_text_from_file(texts, file_path, text_ss->custom_delim); } else { load_text_from_file(texts, file_path); } } } }obs-text-slideshow-1.5.2/src/files.h000066400000000000000000000003211415025745200173260ustar00rootroot00000000000000#pragma once #include #include "obs-module.h" #include "obs-text-slideshow.h" using std::vector; void read_file(struct text_slideshow *text_ss, obs_data_t *settings, vector &texts);obs-text-slideshow-1.5.2/src/obs-text-freetype2-slideshow.cpp000066400000000000000000000206461415025745200242420ustar00rootroot00000000000000/* obs-text-slideshow Copyright (C) 2021 Joshua Wong jbwong05@gmail.com 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, see */ #include #include "obs-text-slideshow.h" #include "files.h" #define S_FONT "font" #define S_TEXT "text" #define S_FROM_FILE "from_file" #define S_ANTIALIASING "antialiasing" #define S_LOG_MODE "log_mode" #define S_LOG_LINES "log_lines" #define S_TEXT_FILE "text_file" #define S_COLOR_1 "color1" #define S_COLOR_2 "color2" #define S_OUTLINE "outline" #define S_DROP_SHADOW "drop_shadow" #define S_CUSTOM_WIDTH "custom_width" #define S_WORD_WRAP "word_wrap" #define S_FACE "face" #define S_SIZE "size" #define S_FLAGS "flags" #define S_STYLE "style" #define T_TXT_(text) obs_module_text("Text." text) #define T_FONT T_TXT_("Font") #define T_TEXT T_TXT_("Text") #define T_FROM_FILE T_TXT_("ReadFromFile") #define T_ANTIALIASING T_TXT_("Antialiasing") #define T_LOG_MODE T_TXT_("ChatLogMode") #define T_LOG_LINES T_TXT_("ChatLogLines") #define T_TEXT_FILE T_TXT_("TextFile") #define T_TEXT_FILE_FILTER T_TXT_("TextFileFilter") #define T_COLOR_1 T_TXT_("Color1") #define T_COLOR_2 T_TXT_("Color2") #define T_OUTLINE T_TXT_("Outline") #define T_DROP_SHADOW T_TXT_("DropShadow") #define T_CUSTOM_WIDTH T_TXT_("CustomWidth") #define T_WORD_WRAP T_TXT_("WordWrap") #ifdef _WIN32 #define DEFAULT_FACE "Arial" #elif __APPLE__ #define DEFAULT_FACE "Helvetica" #else #define DEFAULT_FACE "Sans Serif" #endif static const char *freetype2_getname(void *unused) { UNUSED_PARAMETER(unused); return obs_module_text("TextFreetype2Slideshow"); } static obs_source_t *create_freetype2(const char *file_path, const char *text, obs_data_t *text_ss_settings) { obs_data_t *settings = obs_data_create(); obs_source_t *source; obs_data_t *curr_font = obs_data_get_obj(text_ss_settings, S_FONT); obs_data_set_obj(settings, S_FONT, curr_font); obs_data_set_bool(settings, S_DROP_SHADOW, obs_data_get_bool(text_ss_settings, S_DROP_SHADOW)); obs_data_set_bool(settings, S_OUTLINE, obs_data_get_bool(text_ss_settings, S_OUTLINE)); obs_data_set_bool(settings, S_WORD_WRAP, obs_data_get_bool(text_ss_settings, S_WORD_WRAP)); obs_data_set_int(settings, S_COLOR_1, obs_data_get_int(text_ss_settings, S_COLOR_1)); obs_data_set_int(settings, S_COLOR_2, obs_data_get_int(text_ss_settings, S_COLOR_2)); obs_data_set_int(settings, S_CUSTOM_WIDTH, obs_data_get_int(text_ss_settings, S_CUSTOM_WIDTH)); obs_data_set_bool(settings, S_FROM_FILE, file_path); obs_data_set_bool(settings, S_LOG_MODE, obs_data_get_bool(text_ss_settings, S_LOG_MODE)); obs_data_set_int(settings, S_LOG_LINES, obs_data_get_int(text_ss_settings, S_LOG_LINES)); obs_data_set_bool(settings, S_ANTIALIASING, obs_data_get_bool(text_ss_settings, S_ANTIALIASING)); obs_data_set_string(settings, S_TEXT_FILE, file_path); obs_data_set_string(settings, S_TEXT, text); source = obs_source_create_private("text_ft2_source", text, settings); obs_data_release(curr_font); obs_data_release(settings); return source; } inline static void update_freetype2_alignment(obs_source_t *transition, obs_data_t *text_ss_settings) { obs_transition_set_alignment(transition, OBS_ALIGN_CENTER); } static void freetype2_update(void *data, obs_data_t *settings) { text_ss_update(data, settings, create_freetype2, update_freetype2_alignment); } static void text_defaults(obs_data_t *settings) { obs_data_t *font_obj = obs_data_create(); // Currently only supporting text freetype2 const uint16_t font_size = 256; obs_data_set_default_string(font_obj, S_FACE, DEFAULT_FACE); obs_data_set_default_int(font_obj, S_SIZE, font_size); obs_data_set_default_int(font_obj, S_FLAGS, 0); obs_data_set_default_string(font_obj, S_STYLE, ""); obs_data_set_default_obj(settings, S_FONT, font_obj); obs_data_set_default_bool(settings, S_ANTIALIASING, true); obs_data_set_default_bool(settings, S_WORD_WRAP, false); obs_data_set_default_bool(settings, S_OUTLINE, false); obs_data_set_default_bool(settings, S_DROP_SHADOW, false); obs_data_set_default_int(settings, S_LOG_LINES, 6); obs_data_set_default_int(settings, S_COLOR_1, 0xFFFFFFFF); obs_data_set_default_int(settings, S_COLOR_2, 0xFFFFFFFF); obs_data_release(font_obj); } static void freetype2_defaults(obs_data_t *settings) { ss_defaults(settings); text_defaults(settings); } static void text_properties(obs_properties_t *props) { // TODO: // Scrolling. Can't think of a way to do it with the render // targets currently being broken. (0.4.2) // Better/pixel shader outline/drop shadow // Some way to pull text files from network, I dunno obs_properties_add_font(props, S_FONT, T_FONT); obs_properties_add_bool(props, S_ANTIALIASING, T_ANTIALIASING); obs_properties_add_bool(props, S_LOG_MODE, T_LOG_MODE); obs_properties_add_int(props, S_LOG_LINES, T_LOG_LINES, 1, 1000, 1); // Version must be at least 27.1 if (obs_get_version() >= ((27 << 24) | (1 << 16))) { obs_properties_add_color_alpha(props, S_COLOR_1, T_COLOR_1); obs_properties_add_color_alpha(props, S_COLOR_2, T_COLOR_2); } else { obs_properties_add_color(props, S_COLOR_1, T_COLOR_1); obs_properties_add_color(props, S_COLOR_2, T_COLOR_2); } obs_properties_add_bool(props, S_OUTLINE, T_OUTLINE); obs_properties_add_bool(props, S_DROP_SHADOW, T_DROP_SHADOW); obs_properties_add_int(props, S_CUSTOM_WIDTH, T_CUSTOM_WIDTH, 0, 4096, 1); obs_properties_add_bool(props, S_WORD_WRAP, T_WORD_WRAP); } static obs_properties_t *freetype2_properties(void *data) { obs_properties_t *props = obs_properties_create(); ss_properites(data, props); text_properties(props); return props; } static void missing_file_callback(void *src, const char *new_path, void *data) { struct text_slideshow *text_ss = (struct text_slideshow *)src; obs_source_t *source = text_ss->source; obs_data_t *settings = obs_source_get_settings(source); obs_data_set_string(settings, S_TEXT_FILE, new_path); obs_source_update(source, settings); obs_data_release(settings); UNUSED_PARAMETER(data); } static obs_missing_files_t *freetype2_missing_files(void *data) { struct text_slideshow *text_ss = (struct text_slideshow *)data; obs_missing_files_t *files = obs_missing_files_create(); obs_source_t *source = text_ss->source; obs_data_t *settings = obs_source_get_settings(source); bool read = obs_data_get_bool(settings, S_FROM_FILE); const char *path = obs_data_get_string(settings, S_TEXT_FILE); if (read && strcmp(path, "") != 0) { if (!os_file_exists(path)) { obs_missing_file_t *file = obs_missing_file_create( path, missing_file_callback, OBS_MISSING_FILE_SOURCE, text_ss->source, NULL); obs_missing_files_add_file(files, file); } } obs_data_release(settings); return files; } void load_text_freetype2_slideshow() { obs_source_info info = {}; info.id = "text-freetype2-slideshow"; info.type = OBS_SOURCE_TYPE_INPUT; info.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW | OBS_SOURCE_COMPOSITE | #ifdef _WIN32 OBS_SOURCE_DEPRECATED | #endif OBS_SOURCE_CONTROLLABLE_MEDIA; info.get_properties = freetype2_properties; info.icon_type = OBS_ICON_TYPE_SLIDESHOW; info.get_name = freetype2_getname; info.create = text_ss_create; info.destroy = text_ss_destroy; info.get_width = text_ss_width; info.get_height = text_ss_height; info.get_defaults = freetype2_defaults; info.update = freetype2_update; info.activate = text_ss_activate; info.deactivate = text_ss_deactivate; info.video_tick = text_ss_video_tick; info.video_render = text_ss_video_render; info.enum_active_sources = text_ss_enum_sources; info.audio_render = text_ss_audio_render; info.media_play_pause = text_ss_play_pause; info.media_restart = text_ss_restart; info.media_stop = text_ss_stop; info.media_next = text_ss_next_slide; info.media_previous = text_ss_previous_slide; info.media_get_state = text_ss_get_state; info.missing_files = freetype2_missing_files; obs_register_source(&info); }obs-text-slideshow-1.5.2/src/obs-text-gdiplus-slideshow.cpp000066400000000000000000000373111415025745200240010ustar00rootroot00000000000000/* obs-text-slideshow Copyright (C) 2021 Joshua Wong jbwong05@gmail.com 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, see */ #include #include #include "obs-text-slideshow.h" #include "files.h" // text gdiplus #define S_FONT "font" #define S_USE_FILE "read_from_file" #define S_FILE "file" #define S_TEXT "text" #define S_COLOR "color" #define S_GRADIENT "gradient" #define S_GRADIENT_COLOR "gradient_color" #define S_GRADIENT_DIR "gradient_dir" #define S_GRADIENT_OPACITY "gradient_opacity" #define S_ALIGN "align" #define S_VALIGN "valign" #define S_OPACITY "opacity" #define S_BKCOLOR "bk_color" #define S_BKOPACITY "bk_opacity" #define S_VERTICAL "vertical" #define S_OUTLINE "outline" #define S_OUTLINE_SIZE "outline_size" #define S_OUTLINE_COLOR "outline_color" #define S_OUTLINE_OPACITY "outline_opacity" #define S_CHATLOG_MODE "chatlog" #define S_CHATLOG_LINES "chatlog_lines" #define S_EXTENTS "extents" #define S_EXTENTS_WRAP "extents_wrap" #define S_EXTENTS_CX "extents_cx" #define S_EXTENTS_CY "extents_cy" #define S_TRANSFORM "transform" #define S_ANTIALIASING "antialiasing" #define S_ALIGN_LEFT "left" #define S_ALIGN_CENTER "center" #define S_ALIGN_RIGHT "right" #define S_VALIGN_TOP "top" #define S_VALIGN_CENTER S_ALIGN_CENTER #define S_VALIGN_BOTTOM "bottom" #define S_TRANSFORM_NONE 0 #define S_TRANSFORM_UPPERCASE 1 #define S_TRANSFORM_LOWERCASE 2 #define S_TRANSFORM_STARTCASE 3 #define S_ANTIALIASING_NONE 0 #define S_ANTIALIASING_STANDARD 1 // text gdiplus #define T_(v) obs_module_text("Text." v) #define T_FONT T_("Font") #define T_TEXT T_("Text") #define T_COLOR T_("Color") #define T_GRADIENT T_("Gradient") #define T_GRADIENT_COLOR T_("Gradient.Color") #define T_GRADIENT_DIR T_("Gradient.Direction") #define T_GRADIENT_OPACITY T_("Gradient.Opacity") #define T_ALIGN T_("Alignment") #define T_VALIGN T_("VerticalAlignment") #define T_OPACITY T_("Opacity") #define T_BKCOLOR T_("BkColor") #define T_BKOPACITY T_("BkOpacity") #define T_VERTICAL T_("Vertical") #define T_OUTLINE T_("Outline") #define T_OUTLINE_SIZE T_("Outline.Size") #define T_OUTLINE_COLOR T_("Outline.Color") #define T_OUTLINE_OPACITY T_("Outline.Opacity") #define T_CHATLOG_MODE T_("ChatlogMode") #define T_CHATLOG_LINES T_("ChatlogMode.Lines") #define T_EXTENTS T_("UseCustomExtents") #define T_EXTENTS_WRAP T_("UseCustomExtents.Wrap") #define T_EXTENTS_CX T_("Width") #define T_EXTENTS_CY T_("Height") #define T_TRANSFORM T_("Transform") #define T_ANTIALIASING T_("Antialiasing") #define T_ALIGN_LEFT T_("Alignment.Left") #define T_ALIGN_CENTER T_("Alignment.Center") #define T_ALIGN_RIGHT T_("Alignment.Right") #define T_VALIGN_TOP T_("VerticalAlignment.Top") #define T_VALIGN_CENTER T_ALIGN_CENTER #define T_VALIGN_BOTTOM T_("VerticalAlignment.Bottom") #define T_TRANSFORM_NONE T_("Transform.None") #define T_TRANSFORM_UPPERCASE T_("Transform.Uppercase") #define T_TRANSFORM_LOWERCASE T_("Transform.Lowercase") #define T_TRANSFORM_STARTCASE T_("Transform.Startcase") static const char *gdiplus_getname(void *unused) { UNUSED_PARAMETER(unused); return obs_module_text("TextGdiplusSlideshow"); } #define obs_data_get_uint32 (uint32_t) obs_data_get_int #define obs_data_set_uint32 obs_data_set_int static obs_source_t *create_gdiplus(const char *file_path, const char *text, obs_data_t *text_ss_settings) { obs_data_t *settings = obs_data_create(); obs_source_t *source; obs_data_t *curr_font = obs_data_get_obj(text_ss_settings, S_FONT); obs_data_set_string(settings, S_TEXT, text); obs_data_set_obj(settings, S_FONT, curr_font); obs_data_set_string(settings, S_ALIGN, obs_data_get_string(text_ss_settings, S_ALIGN)); obs_data_set_string(settings, S_VALIGN, obs_data_get_string(text_ss_settings, S_VALIGN)); obs_data_set_uint32(settings, S_COLOR, obs_data_get_uint32(text_ss_settings, S_COLOR)); obs_data_set_uint32(settings, S_OPACITY, obs_data_get_uint32(text_ss_settings, S_OPACITY)); obs_data_set_bool(settings, S_GRADIENT, obs_data_get_bool(text_ss_settings, S_GRADIENT)); obs_data_set_uint32(settings, S_GRADIENT_COLOR, obs_data_get_uint32(text_ss_settings, S_GRADIENT_COLOR)); obs_data_set_uint32(settings, S_GRADIENT_OPACITY, obs_data_get_uint32(text_ss_settings, S_GRADIENT_OPACITY)); obs_data_set_double(settings, S_GRADIENT_DIR, obs_data_get_double(text_ss_settings, S_GRADIENT_DIR)); obs_data_set_bool(settings, S_VERTICAL, obs_data_get_bool(text_ss_settings, S_VERTICAL)); obs_data_set_bool(settings, S_OUTLINE, obs_data_get_bool(text_ss_settings, S_OUTLINE)); obs_data_set_uint32(settings, S_OUTLINE_COLOR, obs_data_get_uint32(text_ss_settings, S_OUTLINE_COLOR)); obs_data_set_uint32(settings, S_OUTLINE_OPACITY, obs_data_get_uint32(text_ss_settings, S_OUTLINE_OPACITY)); obs_data_set_uint32(settings, S_OUTLINE_SIZE, obs_data_get_uint32(text_ss_settings, S_OUTLINE_SIZE)); obs_data_set_bool(settings, S_USE_FILE, file_path); obs_data_set_string(settings, S_FILE, file_path); obs_data_set_bool(settings, S_CHATLOG_MODE, obs_data_get_bool(text_ss_settings, S_CHATLOG_MODE)); obs_data_set_int(settings, S_CHATLOG_LINES, obs_data_get_int(text_ss_settings, S_CHATLOG_LINES)); obs_data_set_bool(settings, S_EXTENTS, obs_data_get_bool(text_ss_settings, S_EXTENTS)); obs_data_set_bool(settings, S_EXTENTS_WRAP, obs_data_get_bool(text_ss_settings, S_EXTENTS_WRAP)); obs_data_set_uint32(settings, S_EXTENTS_CX, obs_data_get_uint32(text_ss_settings, S_EXTENTS_CX)); obs_data_set_uint32(settings, S_EXTENTS_CY, obs_data_get_uint32(text_ss_settings, S_EXTENTS_CY)); obs_data_set_int(settings, S_TRANSFORM, obs_data_get_int(text_ss_settings, S_TRANSFORM)); obs_data_set_bool(settings, S_ANTIALIASING, obs_data_get_bool(text_ss_settings, S_ANTIALIASING)); obs_data_set_uint32(settings, S_BKCOLOR, obs_data_get_uint32(text_ss_settings, S_BKCOLOR)); obs_data_set_uint32(settings, S_BKOPACITY, obs_data_get_uint32(text_ss_settings, S_BKOPACITY)); source = obs_source_create_private("text_gdiplus", text, settings); obs_data_release(curr_font); obs_data_release(settings); return source; } static void update_gdiplus_alignment(obs_source_t *transition, obs_data_t *text_ss_settings) { const char *align_str = obs_data_get_string(text_ss_settings, S_ALIGN); if (strcmp(align_str, S_ALIGN_CENTER) == 0) { obs_transition_set_alignment(transition, OBS_ALIGN_CENTER); } else if (strcmp(align_str, S_ALIGN_RIGHT) == 0) { obs_transition_set_alignment(transition, OBS_ALIGN_RIGHT); } else { obs_transition_set_alignment(transition, OBS_ALIGN_LEFT); } } static void gdiplus_update(void *data, obs_data_t *settings) { text_ss_update(data, settings, create_gdiplus, update_gdiplus_alignment); } static void text_defaults(obs_data_t *settings) { obs_data_t *font_obj = obs_data_create(); obs_data_set_default_string(font_obj, "face", "Arial"); // Only non-obsolete v2 atm obs_data_set_default_int(font_obj, "size", 256); obs_data_set_default_obj(settings, S_FONT, font_obj); obs_data_set_default_string(settings, S_ALIGN, S_ALIGN_LEFT); obs_data_set_default_string(settings, S_VALIGN, S_VALIGN_TOP); obs_data_set_default_int(settings, S_COLOR, 0xFFFFFF); obs_data_set_default_int(settings, S_OPACITY, 100); obs_data_set_default_int(settings, S_GRADIENT_COLOR, 0xFFFFFF); obs_data_set_default_int(settings, S_GRADIENT_OPACITY, 100); obs_data_set_default_double(settings, S_GRADIENT_DIR, 90.0); obs_data_set_default_int(settings, S_BKCOLOR, 0x000000); obs_data_set_default_int(settings, S_BKOPACITY, 0); obs_data_set_default_int(settings, S_OUTLINE_SIZE, 2); obs_data_set_default_int(settings, S_OUTLINE_COLOR, 0xFFFFFF); obs_data_set_default_int(settings, S_OUTLINE_OPACITY, 100); obs_data_set_default_int(settings, S_CHATLOG_LINES, 6); obs_data_set_default_bool(settings, S_EXTENTS_WRAP, true); obs_data_set_default_int(settings, S_EXTENTS_CX, 100); obs_data_set_default_int(settings, S_EXTENTS_CY, 100); obs_data_set_default_int(settings, S_TRANSFORM, S_TRANSFORM_NONE); obs_data_set_default_bool(settings, S_ANTIALIASING, true); obs_data_release(font_obj); } static void gdiplus_defaults(obs_data_t *settings) { ss_defaults(settings); text_defaults(settings); } static bool outline_changed(obs_properties_t *props, obs_property_t *p, obs_data_t *s) { bool outline = obs_data_get_bool(s, S_OUTLINE); set_vis(S_OUTLINE_SIZE, outline); set_vis(S_OUTLINE_COLOR, outline); set_vis(S_OUTLINE_OPACITY, outline); return true; } static bool chatlog_mode_changed(obs_properties_t *props, obs_property_t *p, obs_data_t *s) { bool chatlog_mode = obs_data_get_bool(s, S_CHATLOG_MODE); set_vis(S_CHATLOG_LINES, chatlog_mode); return true; } static bool gradient_changed(obs_properties_t *props, obs_property_t *p, obs_data_t *s) { bool gradient = obs_data_get_bool(s, S_GRADIENT); set_vis(S_GRADIENT_COLOR, gradient); set_vis(S_GRADIENT_OPACITY, gradient); set_vis(S_GRADIENT_DIR, gradient); return true; } static bool extents_modified(obs_properties_t *props, obs_property_t *p, obs_data_t *s) { bool use_extents = obs_data_get_bool(s, S_EXTENTS); set_vis(S_EXTENTS_WRAP, use_extents); set_vis(S_EXTENTS_CX, use_extents); set_vis(S_EXTENTS_CY, use_extents); return true; } #undef set_vis static void text_properties(obs_properties_t *props) { obs_property_t *p; obs_properties_add_font(props, S_FONT, T_FONT); obs_properties_add_bool(props, S_ANTIALIASING, T_ANTIALIASING); p = obs_properties_add_list(props, S_TRANSFORM, T_TRANSFORM, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, T_TRANSFORM_NONE, S_TRANSFORM_NONE); obs_property_list_add_int(p, T_TRANSFORM_UPPERCASE, S_TRANSFORM_UPPERCASE); obs_property_list_add_int(p, T_TRANSFORM_LOWERCASE, S_TRANSFORM_LOWERCASE); obs_property_list_add_int(p, T_TRANSFORM_STARTCASE, S_TRANSFORM_STARTCASE); obs_properties_add_bool(props, S_VERTICAL, T_VERTICAL); obs_properties_add_color(props, S_COLOR, T_COLOR); p = obs_properties_add_int_slider(props, S_OPACITY, T_OPACITY, 0, 100, 1); obs_property_int_set_suffix(p, "%"); p = obs_properties_add_bool(props, S_GRADIENT, T_GRADIENT); obs_property_set_modified_callback(p, gradient_changed); obs_properties_add_color(props, S_GRADIENT_COLOR, T_GRADIENT_COLOR); p = obs_properties_add_int_slider(props, S_GRADIENT_OPACITY, T_GRADIENT_OPACITY, 0, 100, 1); obs_property_int_set_suffix(p, "%"); obs_properties_add_float_slider(props, S_GRADIENT_DIR, T_GRADIENT_DIR, 0, 360, 0.1); obs_properties_add_color(props, S_BKCOLOR, T_BKCOLOR); p = obs_properties_add_int_slider(props, S_BKOPACITY, T_BKOPACITY, 0, 100, 1); obs_property_int_set_suffix(p, "%"); p = obs_properties_add_list(props, S_ALIGN, T_ALIGN, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); obs_property_list_add_string(p, T_ALIGN_LEFT, S_ALIGN_LEFT); obs_property_list_add_string(p, T_ALIGN_CENTER, S_ALIGN_CENTER); obs_property_list_add_string(p, T_ALIGN_RIGHT, S_ALIGN_RIGHT); p = obs_properties_add_list(props, S_VALIGN, T_VALIGN, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); obs_property_list_add_string(p, T_VALIGN_TOP, S_VALIGN_TOP); obs_property_list_add_string(p, T_VALIGN_CENTER, S_VALIGN_CENTER); obs_property_list_add_string(p, T_VALIGN_BOTTOM, S_VALIGN_BOTTOM); p = obs_properties_add_bool(props, S_OUTLINE, T_OUTLINE); obs_property_set_modified_callback(p, outline_changed); obs_properties_add_int(props, S_OUTLINE_SIZE, T_OUTLINE_SIZE, 1, 20, 1); obs_properties_add_color(props, S_OUTLINE_COLOR, T_OUTLINE_COLOR); p = obs_properties_add_int_slider(props, S_OUTLINE_OPACITY, T_OUTLINE_OPACITY, 0, 100, 1); obs_property_int_set_suffix(p, "%"); p = obs_properties_add_bool(props, S_CHATLOG_MODE, T_CHATLOG_MODE); obs_property_set_modified_callback(p, chatlog_mode_changed); obs_properties_add_int(props, S_CHATLOG_LINES, T_CHATLOG_LINES, 1, 1000, 1); p = obs_properties_add_bool(props, S_EXTENTS, T_EXTENTS); obs_property_set_modified_callback(p, extents_modified); obs_properties_add_int(props, S_EXTENTS_CX, T_EXTENTS_CX, 32, 8000, 1); obs_properties_add_int(props, S_EXTENTS_CY, T_EXTENTS_CY, 32, 8000, 1); obs_properties_add_bool(props, S_EXTENTS_WRAP, T_EXTENTS_WRAP); } static obs_properties_t *gdiplus_properties(void *data) { obs_properties_t *props = obs_properties_create(); ss_properites(data, props); text_properties(props); return props; } static void missing_file_callback(void *src, const char *new_path, void *data) { struct text_slideshow *text_ss = (struct text_slideshow *)src; obs_source_t *source = text_ss->source; obs_data_t *settings = obs_source_get_settings(source); obs_data_set_string(settings, S_FILE, new_path); obs_source_update(source, settings); obs_data_release(settings); UNUSED_PARAMETER(data); } static obs_missing_files_t *gdiplus_missing_files(void *data) { struct text_slideshow *text_ss = (struct text_slideshow *)data; obs_missing_files_t *files = obs_missing_files_create(); obs_source_t *source = text_ss->source; obs_data_t *settings = obs_source_get_settings(source); bool read = obs_data_get_bool(settings, S_USE_FILE); const char *path = obs_data_get_string(settings, S_FILE); if (read && strcmp(path, "") != 0) { if (!os_file_exists(path)) { obs_missing_file_t *file = obs_missing_file_create( path, missing_file_callback, OBS_MISSING_FILE_SOURCE, text_ss->source, NULL); obs_missing_files_add_file(files, file); } } obs_data_release(settings); return files; } static bool enum_callback(void *param, obs_source_t *source) { const char *id = obs_source_get_id(source); if (strcmp(id, "text-gdiplus-slideshow") == 0) { obs_data_t *settings = obs_source_get_settings(source); obs_source_update(source, settings); obs_data_release(settings); } return true; } static void obs_frontend_event_wrapper(enum obs_frontend_event event, void *ptr) { if (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) { obs_enum_sources(enum_callback, NULL); } } void load_text_gdiplus_slideshow() { obs_source_info info = {}; info.id = "text-gdiplus-slideshow"; info.type = OBS_SOURCE_TYPE_INPUT; info.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW | OBS_SOURCE_COMPOSITE | OBS_SOURCE_CONTROLLABLE_MEDIA; info.get_properties = gdiplus_properties; info.icon_type = OBS_ICON_TYPE_SLIDESHOW; info.get_name = gdiplus_getname; info.create = text_ss_create; info.destroy = text_ss_destroy; info.get_width = text_ss_width; info.get_height = text_ss_height; info.get_defaults = gdiplus_defaults; info.update = gdiplus_update; info.activate = text_ss_activate; info.deactivate = text_ss_deactivate; info.video_tick = text_ss_video_tick; info.video_render = text_ss_video_render; info.enum_active_sources = text_ss_enum_sources; info.audio_render = text_ss_audio_render; info.media_play_pause = text_ss_play_pause; info.media_restart = text_ss_restart; info.media_stop = text_ss_stop; info.media_next = text_ss_next_slide; info.media_previous = text_ss_previous_slide; info.media_get_state = text_ss_get_state; info.missing_files = gdiplus_missing_files; obs_register_source(&info); obs_frontend_add_event_callback(obs_frontend_event_wrapper, NULL); }obs-text-slideshow-1.5.2/src/obs-text-slideshow-dock.cpp000066400000000000000000000217131415025745200232510ustar00rootroot00000000000000/* obs-text-slideshow Copyright (C) 2021 Joshua Wong jbwong05@gmail.com 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, see */ #include "obs-text-slideshow-dock.h" #include using std::unordered_set; struct found_text_slideshows { vector *ordered_slideshows; unordered_set found_slideshows; }; void TextSlideshowDock::OBSFrontendEventWrapper(enum obs_frontend_event event, void *ptr) { TextSlideshowDock *dock = reinterpret_cast(ptr); dock->OBSFrontendEvent(event); } static bool findTextSlideshowSources(obs_scene_t *scene, obs_sceneitem_t *item, void *param) { obs_source_t *source = NULL; source = obs_sceneitem_get_source(item); if (source) { const char *id = obs_source_get_id(source); if (strcmp(id, "text-freetype2-slideshow") == 0 #ifdef _WIN32 || strcmp(id, "text-gdiplus-slideshow") == 0 #endif ) { struct found_text_slideshows *found_text_slideshows = reinterpret_cast( param); if (found_text_slideshows->found_slideshows.find( source) == found_text_slideshows->found_slideshows.end()) { found_text_slideshows->ordered_slideshows ->insert(found_text_slideshows ->ordered_slideshows ->begin(), source); found_text_slideshows->found_slideshows.insert( source); } } else if (strcmp(id, "scene") == 0) { obs_scene_t *nested_scene = obs_scene_from_source(source); if (nested_scene) { struct found_text_slideshows * found_text_slideshows = reinterpret_cast< struct found_text_slideshows *>( param); obs_scene_enum_items(nested_scene, findTextSlideshowSources, found_text_slideshows); } } } return true; } void TextSlideshowDock::OBSFrontendEvent(enum obs_frontend_event event) { switch (event) { case OBS_FRONTEND_EVENT_FINISHED_LOADING: case OBS_FRONTEND_EVENT_SCENE_CHANGED: case OBS_FRONTEND_EVENT_STUDIO_MODE_ENABLED: case OBS_FRONTEND_EVENT_STUDIO_MODE_DISABLED: refreshProgram(); case OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED: refreshPreview(); break; default: break; } } void TextSlideshowDock::changeActivePreviewSource(int index) { if (index >= 0) { setActiveSource(index, ui->previewSourceBox, preview_text_slideshows, &preview_active_slideshow); updateTexts(ui->previewTextList, preview_texts, &preview_active_slideshow); } } void TextSlideshowDock::changeActiveProgramSource(int index) { if (index >= 0) { setActiveSource(index, ui->programSourceBox, program_text_slideshows, &program_active_slideshow); updateTexts(ui->programTextList, program_texts, &program_active_slideshow); } } void TextSlideshowDock::setActiveSource(int index, QComboBox *sourceBox, vector &text_slideshows, struct slideshow_t *active_slideshow) { if (index >= 0 && index < text_slideshows.size()) { active_slideshow->source = text_slideshows[index]; active_slideshow->index = index; sourceBox->setCurrentIndex(index); } else { active_slideshow->source = NULL; active_slideshow->index = -1; } } void TextSlideshowDock::chooseNewActiveSource( QComboBox *sourceBox, vector &text_slideshows, struct slideshow_t *active_slideshow) { if (active_slideshow->index != -1) { setActiveSource(active_slideshow->index, sourceBox, text_slideshows, active_slideshow); } else { active_slideshow->source = NULL; // Find first that is not hidden for (unsigned int i = 0; i < text_slideshows.size() && !active_slideshow->source; i++) { if (!obs_source_is_hidden(text_slideshows[i])) { setActiveSource(i, sourceBox, text_slideshows, active_slideshow); return; } } // Default to first if all hidden if (!active_slideshow->source) { setActiveSource(0, sourceBox, text_slideshows, active_slideshow); } } } void TextSlideshowDock::updateSources(obs_source_t *scene_source, QComboBox *sourceBox, vector &text_slideshows, struct slideshow_t *active_slideshow) { obs_scene_t *scene = NULL; if (!scene_source) { scene_source = obs_frontend_get_current_scene(); } sourceBox->clear(); text_slideshows.clear(); struct found_text_slideshows found_text_slideshows; found_text_slideshows.ordered_slideshows = &text_slideshows; scene = obs_scene_from_source(scene_source); obs_scene_enum_items(scene, findTextSlideshowSources, &found_text_slideshows); active_slideshow->index = -1; for (unsigned int i = 0; i < text_slideshows.size(); i++) { const char *name = obs_source_get_name(text_slideshows[i]); sourceBox->addItem(name); if (active_slideshow->source == text_slideshows[i]) { active_slideshow->index = i; } } if (scene_source) { obs_source_release(scene_source); } } void TextSlideshowDock::updateTexts(QListWidget *textList, vector &texts, struct slideshow_t *active_slideshow) { texts.clear(); proc_handler_t *handler = obs_source_get_proc_handler(active_slideshow->source); calldata_t cd = {0}; calldata_set_ptr(&cd, "texts", &texts); proc_handler_call(handler, "get_texts", &cd); calldata_free(&cd); textList->clear(); for (unsigned int i = 0; i < texts.size(); i++) { textList->addItem(texts[i]); } } void TextSlideshowDock::refreshPreview() { updateSources(obs_frontend_get_current_preview_scene(), ui->previewSourceBox, preview_text_slideshows, &preview_active_slideshow); if (preview_text_slideshows.size() == 0) { ui->previewSourceBox->addItem( "No Text Slide Show sources found on current scene"); ui->previewTextList->clear(); } else { chooseNewActiveSource(ui->previewSourceBox, preview_text_slideshows, &preview_active_slideshow); updateTexts(ui->previewTextList, preview_texts, &preview_active_slideshow); } } void TextSlideshowDock::refreshProgram() { updateSources(obs_frontend_get_current_scene(), ui->programSourceBox, program_text_slideshows, &program_active_slideshow); if (program_text_slideshows.size() == 0) { ui->programSourceBox->addItem( "No Text Slide Show sources found on current scene"); ui->programTextList->clear(); } else { chooseNewActiveSource(ui->programSourceBox, program_text_slideshows, &program_active_slideshow); updateTexts(ui->programTextList, program_texts, &program_active_slideshow); } } void TextSlideshowDock::previewTransition(QListWidgetItem *item) { int index = ui->previewTextList->row(item); if (index >= 0) { proc_handler_t *handler = obs_source_get_proc_handler( preview_active_slideshow.source); calldata_t cd = {0}; calldata_set_int(&cd, "index", index); proc_handler_call(handler, "dock_transition", &cd); calldata_free(&cd); } } void TextSlideshowDock::programTransition(QListWidgetItem *item) { int index = ui->programTextList->row(item); if (index >= 0) { proc_handler_t *handler = obs_source_get_proc_handler( program_active_slideshow.source); calldata_t cd = {0}; calldata_set_int(&cd, "index", index); proc_handler_call(handler, "dock_transition", &cd); calldata_free(&cd); } } static void callback(void *data, calldata_t *cd) { TextSlideshowDock *dock = reinterpret_cast(data); dock->refreshPreview(); dock->refreshProgram(); } TextSlideshowDock::TextSlideshowDock(QWidget *parent) : QDockWidget(parent), ui(new Ui::TextSlideshowDock) { ui->setupUi(this); preview_active_slideshow.source = NULL; preview_active_slideshow.index = -1; program_active_slideshow.source = NULL; program_active_slideshow.index = -1; const char *source_signals[] = {"source_create", "source_destroy", "source_rename", "source_save"}; signal_handler_t *obs_handler = obs_get_signal_handler(); for (int i = 0; i < 4; i++) { signal_handler_connect(obs_handler, source_signals[i], callback, this); } connect(ui->previewSourceBox, QOverload::of(&QComboBox::activated), this, &TextSlideshowDock::changeActivePreviewSource); connect(ui->programSourceBox, QOverload::of(&QComboBox::activated), this, &TextSlideshowDock::changeActiveProgramSource); connect(ui->previewTextList, &QListWidget::itemClicked, this, &TextSlideshowDock::previewTransition); connect(ui->programTextList, &QListWidget::itemClicked, this, &TextSlideshowDock::programTransition); obs_frontend_add_event_callback(OBSFrontendEventWrapper, this); hide(); } TextSlideshowDock::~TextSlideshowDock() {}obs-text-slideshow-1.5.2/src/obs-text-slideshow-dock.h000066400000000000000000000044161415025745200227170ustar00rootroot00000000000000/* obs-text-slideshow Copyright (C) 2021 Joshua Wong jbwong05@gmail.com 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, see */ #pragma once #include #include #include #include #include "ui_obs-text-slideshow-dock.h" using std::vector; struct slideshow_t { obs_source_t *source; int index; }; class TextSlideshowDock : public QDockWidget { Q_OBJECT private: static void OBSFrontendEventWrapper(enum obs_frontend_event event, void *ptr); void OBSFrontendEvent(enum obs_frontend_event event); void changeActivePreviewSource(int index); void changeActiveProgramSource(int index); void setActiveSource(int index, QComboBox *sourceBox, vector &text_slideshows, struct slideshow_t *active_slideshow); void chooseNewActiveSource(QComboBox *sourceBox, vector &text_slideshows, struct slideshow_t *active_slideshow); void updateSources(obs_source_t *scene_source, QComboBox *sourceBox, vector &text_slideshows, struct slideshow_t *active_slideshow); void updateTexts(QListWidget *textList, vector &texts, struct slideshow_t *active_slideshow); void previewTransition(QListWidgetItem *item); void programTransition(QListWidgetItem *item); std::unique_ptr ui; vector preview_text_slideshows; vector preview_texts; struct slideshow_t preview_active_slideshow; vector program_text_slideshows; vector program_texts; struct slideshow_t program_active_slideshow; public: TextSlideshowDock(QWidget *parent = nullptr); ~TextSlideshowDock(); void refreshPreview(); void refreshProgram(); };obs-text-slideshow-1.5.2/src/obs-text-slideshow-dock.ui000066400000000000000000000044061415025745200231040ustar00rootroot00000000000000 TextSlideshowDock 0 0 563 435 Qt::CustomContextMenu true QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetFloatable OBS Text Slide Show Qt::CustomContextMenu 0 0 0 0 0 1 Preview Program obs-text-slideshow-1.5.2/src/obs-text-slideshow.cpp000066400000000000000000000665171415025745200223460ustar00rootroot00000000000000/* obs-text-slideshow Copyright (C) 2021 Joshua Wong jbwong05@gmail.com 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, see */ #include "obs-text-slideshow.h" #include #include #include "files.h" using std::vector; void play_pause_hotkey(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey, bool pressed) { UNUSED_PARAMETER(id); UNUSED_PARAMETER(hotkey); struct text_slideshow *text_ss = (text_slideshow *)data; if (pressed && obs_source_showing(text_ss->source)) obs_source_media_play_pause(text_ss->source, !text_ss->paused); } void restart_hotkey(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey, bool pressed) { UNUSED_PARAMETER(id); UNUSED_PARAMETER(hotkey); struct text_slideshow *text_ss = (text_slideshow *)data; if (pressed && obs_source_showing(text_ss->source)) obs_source_media_restart(text_ss->source); } void stop_hotkey(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey, bool pressed) { UNUSED_PARAMETER(id); UNUSED_PARAMETER(hotkey); struct text_slideshow *text_ss = (text_slideshow *)data; if (pressed && obs_source_showing(text_ss->source)) obs_source_media_stop(text_ss->source); } void next_slide_hotkey(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey, bool pressed) { UNUSED_PARAMETER(id); UNUSED_PARAMETER(hotkey); struct text_slideshow *text_ss = (text_slideshow *)data; if (!text_ss->manual) return; if (pressed && obs_source_showing(text_ss->source)) obs_source_media_next(text_ss->source); } void previous_slide_hotkey(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey, bool pressed) { UNUSED_PARAMETER(id); UNUSED_PARAMETER(hotkey); struct text_slideshow *text_ss = (text_slideshow *)data; if (!text_ss->manual) return; if (pressed && obs_source_showing(text_ss->source)) obs_source_media_previous(text_ss->source); } static obs_source_t *get_source(struct darray *array, const char *file_path, const char *text) { DARRAY(struct text_data) text_srcs; obs_source_t *source = NULL; text_srcs.da = *array; for (size_t i = 0; i < text_srcs.num; i++) { const char *curr_file_path = text_srcs.array[i].file_path; const char *curr_text = text_srcs.array[i].text; if (file_path && curr_file_path && strcmp(file_path, curr_file_path) == 0) { source = text_srcs.array[i].source; obs_source_addref(source); break; } else if (text && curr_text && strcmp(text, curr_text) == 0) { source = text_srcs.array[i].source; obs_source_addref(source); break; } } return source; } static void add_text_src(struct text_slideshow *text_ss, struct darray *array, const char *file_path, const char *text, uint32_t *cx, uint32_t *cy, obs_data_t *settings, text_source_create text_creator) { DARRAY(struct text_data) new_text_data; struct text_data data; obs_source_t *new_source; new_text_data.da = *array; pthread_mutex_lock(&text_ss->mutex); new_source = get_source(&text_ss->text_srcs.da, file_path, text); pthread_mutex_unlock(&text_ss->mutex); if (!new_source) new_source = get_source(&new_text_data.da, file_path, text); if (new_source) obs_source_update(new_source, settings); if (!new_source) new_source = (*text_creator)(file_path, text, settings); if (new_source) { uint32_t new_cx = obs_source_get_width(new_source); uint32_t new_cy = obs_source_get_height(new_source); if (file_path) { data.file_path = bstrdup(file_path); data.text = NULL; } else if (text) { data.file_path = NULL; data.text = bstrdup(text); } data.source = new_source; da_push_back(new_text_data, &data); if (new_cx > *cx) *cx = new_cx; if (new_cy > *cy) *cy = new_cy; } *array = new_text_data.da; } static void free_text_srcs(struct darray *array) { DARRAY(struct text_data) text_srcs; text_srcs.da = *array; for (size_t i = 0; i < text_srcs.num; i++) { if (text_srcs.array[i].file_path) { bfree(text_srcs.array[i].file_path); } if (text_srcs.array[i].text) { bfree(text_srcs.array[i].text); } obs_source_release(text_srcs.array[i].source); } da_free(text_srcs); } void text_ss_destroy(void *data) { struct text_slideshow *text_ss = (text_slideshow *)data; obs_source_release(text_ss->transition); free_text_srcs(&text_ss->text_srcs.da); pthread_mutex_destroy(&text_ss->mutex); pthread_cond_destroy(&text_ss->dock_get_texts); bfree(text_ss); } static void get_texts(void *data, calldata_t *cd) { vector *texts = (vector *)calldata_ptr(cd, "texts"); struct text_slideshow *text_ss = (text_slideshow *)data; pthread_mutex_lock(&text_ss->mutex); if (!text_ss->dock_can_get_texts) { pthread_cond_wait(&text_ss->dock_get_texts, &text_ss->mutex); } DARRAY(struct text_data) text_srcs; text_srcs.da = text_ss->text_srcs.da; for (size_t i = 0; i < text_srcs.num; i++) { if (text_srcs.array[i].text) { texts->push_back(text_srcs.array[i].text); } else if (text_srcs.array[i].file_path) { texts->push_back(text_srcs.array[i].file_path); } } pthread_mutex_unlock(&text_ss->mutex); } static inline bool item_valid(struct text_slideshow *text_ss) { return text_ss->text_srcs.num && text_ss->cur_item < text_ss->text_srcs.num; } static void set_media_state(void *data, enum obs_media_state state) { struct text_slideshow *text_ss = (text_slideshow *)data; text_ss->state = state; } static void do_transition(void *data, bool to_null) { struct text_slideshow *text_ss = (text_slideshow *)data; bool valid = item_valid(text_ss); if (valid && text_ss->use_cut) { obs_transition_set( text_ss->transition, text_ss->text_srcs.array[text_ss->cur_item].source); } else if (valid && !to_null) { obs_transition_start( text_ss->transition, OBS_TRANSITION_MODE_AUTO, text_ss->tr_speed, text_ss->text_srcs.array[text_ss->cur_item].source); } else { obs_transition_start(text_ss->transition, OBS_TRANSITION_MODE_AUTO, text_ss->tr_speed, NULL); set_media_state(text_ss, OBS_MEDIA_STATE_ENDED); obs_source_media_ended(text_ss->source); } } static void dock_transition(void *data, calldata_t *cd) { int index = (int)calldata_int(cd, "index"); struct text_slideshow *text_ss = (text_slideshow *)data; if (!text_ss->text_srcs.num || obs_transition_get_time(text_ss->transition) < 1.0f) return; if (index >= text_ss->text_srcs.num) text_ss->cur_item = 0; else text_ss->cur_item = index; do_transition(text_ss, false); } void *text_ss_create(obs_data_t *settings, obs_source_t *source) { struct text_slideshow *text_ss = (text_slideshow *)bzalloc(sizeof(*text_ss)); text_ss->source = source; text_ss->manual = false; text_ss->paused = false; text_ss->stop = false; text_ss->play_pause_hotkey = obs_hotkey_register_source( source, "SlideShow.PlayPause", obs_module_text("SlideShow.PlayPause"), play_pause_hotkey, text_ss); text_ss->restart_hotkey = obs_hotkey_register_source( source, "SlideShow.Restart", obs_module_text("SlideShow.Restart"), restart_hotkey, text_ss); text_ss->stop_hotkey = obs_hotkey_register_source( source, "SlideShow.Stop", obs_module_text("SlideShow.Stop"), stop_hotkey, text_ss); text_ss->prev_hotkey = obs_hotkey_register_source( source, "SlideShow.NextSlide", obs_module_text("SlideShow.NextSlide"), next_slide_hotkey, text_ss); text_ss->prev_hotkey = obs_hotkey_register_source( source, "SlideShow.PreviousSlide", obs_module_text("SlideShow.PreviousSlide"), previous_slide_hotkey, text_ss); proc_handler_t *handler = obs_source_get_proc_handler(source); proc_handler_add(handler, "void get_texts(ptr texts)", get_texts, text_ss); proc_handler_add(handler, "void dock_transition(int index)", dock_transition, text_ss); pthread_mutex_init_value(&text_ss->mutex); if (pthread_mutex_init(&text_ss->mutex, NULL) != 0) { text_ss_destroy(text_ss); return NULL; } if (pthread_cond_init(&text_ss->dock_get_texts, NULL) != 0) { text_ss_destroy(text_ss); return NULL; } pthread_mutex_lock(&text_ss->mutex); text_ss->dock_can_get_texts = false; pthread_mutex_unlock(&text_ss->mutex); text_ss->settings = settings; obs_source_update(source, settings); return text_ss; } static void free_text_src(struct darray *array) { DARRAY(struct text_data) text_srcs; text_srcs.da = *array; for (size_t i = 0; i < text_srcs.num; i++) { bfree(text_srcs.array[i].text); obs_source_release(text_srcs.array[i].source); } da_free(text_srcs); } static inline size_t random_text_src(struct text_slideshow *text_ss) { return (size_t)rand() % text_ss->text_srcs.num; } static bool valid_extension(const char *ext) { if (!ext) return false; return astrcmpi(ext, ".txt") == 0; } void text_ss_update(void *data, obs_data_t *settings, text_source_create text_creator, set_text_alignment set_alignment) { DARRAY(struct text_data) new_text_srcs; DARRAY(struct text_data) old_text_srcs; obs_source_t *new_tr = NULL; obs_source_t *old_tr = NULL; struct text_slideshow *text_ss = (text_slideshow *)data; obs_data_array_t *text_array; obs_data_array_t *file_array; const char *tr_name; uint32_t new_duration; uint32_t new_speed; uint32_t cx = 0; uint32_t cy = 0; size_t text_count; size_t file_count; const char *behavior; const char *mode; pthread_mutex_lock(&text_ss->mutex); text_ss->dock_can_get_texts = false; pthread_mutex_unlock(&text_ss->mutex); /* ------------------------------------- */ /* get settings data */ da_init(new_text_srcs); behavior = obs_data_get_string(settings, S_BEHAVIOR); if (astrcmpi(behavior, S_BEHAVIOR_PAUSE_UNPAUSE) == 0) text_ss->behavior = BEHAVIOR_PAUSE_UNPAUSE; else if (astrcmpi(behavior, S_BEHAVIOR_ALWAYS_PLAY) == 0) text_ss->behavior = BEHAVIOR_ALWAYS_PLAY; else /* S_BEHAVIOR_STOP_RESTART */ text_ss->behavior = BEHAVIOR_STOP_RESTART; mode = obs_data_get_string(settings, S_MODE); text_ss->manual = (astrcmpi(mode, S_MODE_MANUAL) == 0); tr_name = obs_data_get_string(settings, S_TRANSITION); if (astrcmpi(tr_name, TR_CUT) == 0) tr_name = "cut_transition"; else if (astrcmpi(tr_name, TR_SWIPE) == 0) tr_name = "swipe_transition"; else if (astrcmpi(tr_name, TR_SLIDE) == 0) tr_name = "slide_transition"; else tr_name = "fade_transition"; text_ss->randomize = obs_data_get_bool(settings, S_RANDOMIZE); text_ss->loop = obs_data_get_bool(settings, S_LOOP); text_ss->hide = obs_data_get_bool(settings, S_HIDE); if (!text_ss->tr_name || strcmp(tr_name, text_ss->tr_name) != 0) new_tr = obs_source_create_private(tr_name, NULL, NULL); new_duration = (uint32_t)obs_data_get_int(settings, S_SLIDE_TIME); new_speed = (uint32_t)obs_data_get_int(settings, S_TR_SPEED); text_ss->read_from_single_file = obs_data_get_bool(settings, S_READ_SINGLE_FILE); text_ss->read_from_multiple_files = obs_data_get_bool(settings, S_READ_MULTIPLE_FILES); if (!text_ss->read_from_single_file && !text_ss->read_from_multiple_files) { // image-slideshow recreates private sources every update // can also simply update existing source settings if this method is too // slow text_array = obs_data_get_array(settings, S_TEXTS); text_count = obs_data_array_count(text_array); for (size_t i = 0; i < text_count; i++) { obs_data_t *item = obs_data_array_item(text_array, i); const char *curr_text = obs_data_get_string(item, "value"); add_text_src(text_ss, &new_text_srcs.da, NULL, curr_text, &cx, &cy, settings, text_creator); obs_data_release(item); } obs_data_array_release(text_array); } if (text_ss->read_from_single_file) { const char *file = obs_data_get_string(settings, S_TXT_FILE); if (strcmp(file, "") != 0) { text_ss->file = file; text_ss->custom_delim = obs_data_get_bool(settings, S_CUSTOM_DELIM) ? obs_data_get_string(settings, S_DELIM) : NULL; // read file vector texts; read_file(text_ss, settings, texts); // add text source for every text read for (unsigned int i = 0; i < texts.size(); i++) { add_text_src(text_ss, &new_text_srcs.da, NULL, texts[i], &cx, &cy, settings, text_creator); bfree((void *)texts[i]); } } } if (text_ss->read_from_multiple_files) { file_array = obs_data_get_array(settings, S_FILES); file_count = obs_data_array_count(file_array); for (size_t i = 0; i < file_count; i++) { obs_data_t *item = obs_data_array_item(file_array, i); const char *path = obs_data_get_string(item, "value"); os_dir_t *dir = os_opendir(path); if (dir) { struct dstr dir_path = {0}; struct os_dirent *ent; for (;;) { const char *ext; ent = os_readdir(dir); if (!ent) break; if (ent->directory) continue; ext = os_get_path_extension( ent->d_name); if (!valid_extension(ext)) continue; dstr_copy(&dir_path, path); dstr_cat_ch(&dir_path, '/'); dstr_cat(&dir_path, ent->d_name); add_text_src(text_ss, &new_text_srcs.da, dir_path.array, NULL, &cx, &cy, settings, text_creator); } dstr_free(&dir_path); os_closedir(dir); } else { add_text_src(text_ss, &new_text_srcs.da, path, NULL, &cx, &cy, settings, text_creator); } obs_data_release(item); } obs_data_array_release(file_array); } /* ------------------------------------- */ /* update settings data */ pthread_mutex_lock(&text_ss->mutex); old_text_srcs.da = text_ss->text_srcs.da; text_ss->text_srcs.da = new_text_srcs.da; if (new_tr) { old_tr = text_ss->transition; text_ss->transition = new_tr; } if (strcmp(tr_name, "cut_transition") != 0) { if (new_duration < 100) new_duration = 100; new_duration += new_speed; } else { if (new_duration < 50) new_duration = 50; } text_ss->tr_speed = new_speed; text_ss->tr_name = tr_name; text_ss->slide_time = (float)new_duration / 1000.0f; text_ss->dock_can_get_texts = true; pthread_cond_signal(&text_ss->dock_get_texts); pthread_mutex_unlock(&text_ss->mutex); /* ------------------------------------- */ /* clean up and restart transition */ if (old_tr) obs_source_release(old_tr); free_text_srcs(&old_text_srcs.da); /* ------------------------- */ const char *res_str = obs_data_get_string(settings, S_CUSTOM_SIZE); bool aspect_only = false, use_auto = true; int cx_in = 0, cy_in = 0; if (strcmp(res_str, T_CUSTOM_SIZE_AUTO) != 0) { #ifdef _WIN32 int ret = sscanf_s(res_str, "%dx%d", &cx_in, &cy_in); #else int ret = sscanf(res_str, "%dx%d", &cx_in, &cy_in); #endif if (ret == 2) { aspect_only = false; use_auto = false; } else { #ifdef _WIN32 ret = sscanf_s(res_str, "%d:%d", &cx_in, &cy_in); #else ret = sscanf(res_str, "%d:%d", &cx_in, &cy_in); #endif if (ret == 2) { aspect_only = true; use_auto = false; } } } if (!use_auto) { double cx_f = (double)cx; double cy_f = (double)cy; double old_aspect = cx_f / cy_f; double new_aspect = (double)cx_in / (double)cy_in; if (aspect_only) { if (fabs(old_aspect - new_aspect) > EPSILON) { if (new_aspect > old_aspect) cx = (uint32_t)(cy_f * new_aspect); else cy = (uint32_t)(cx_f / new_aspect); } } else { cx = (uint32_t)cx_in; cy = (uint32_t)cy_in; } } /* ------------------------- */ text_ss->cx = cx; text_ss->cy = cy; text_ss->cur_item = 0; text_ss->elapsed = 0.0f; obs_transition_set_size(text_ss->transition, cx, cy); (*set_alignment)(text_ss->transition, settings); obs_transition_set_scale_type(text_ss->transition, OBS_TRANSITION_SCALE_ASPECT); if (text_ss->randomize && text_ss->text_srcs.num) text_ss->cur_item = random_text_src(text_ss); if (new_tr) obs_source_add_active_child(text_ss->source, new_tr); if (text_ss->text_srcs.num) { do_transition(text_ss, false); if (text_ss->manual) set_media_state(text_ss, OBS_MEDIA_STATE_PAUSED); else set_media_state(text_ss, OBS_MEDIA_STATE_PLAYING); obs_source_media_started(text_ss->source); } } void text_ss_activate(void *data) { struct text_slideshow *text_ss = (text_slideshow *)data; if (text_ss->behavior == BEHAVIOR_STOP_RESTART) { text_ss->restart_on_activate = true; text_ss->use_cut = true; } else if (text_ss->behavior == BEHAVIOR_PAUSE_UNPAUSE) { text_ss->pause_on_deactivate = false; } } void text_ss_deactivate(void *data) { struct text_slideshow *text_ss = (text_slideshow *)data; if (text_ss->behavior == BEHAVIOR_PAUSE_UNPAUSE) text_ss->pause_on_deactivate = true; } static obs_source_t *get_transition(struct text_slideshow *text_ss) { obs_source_t *tr; pthread_mutex_lock(&text_ss->mutex); tr = text_ss->transition; obs_source_addref(tr); pthread_mutex_unlock(&text_ss->mutex); return tr; } void text_ss_video_render(void *data, gs_effect_t *effect) { struct text_slideshow *text_ss = (text_slideshow *)data; obs_source_t *transition = get_transition(text_ss); if (transition) { obs_source_video_render(transition); obs_source_release(transition); } UNUSED_PARAMETER(effect); } void text_ss_video_tick(void *data, float seconds) { struct text_slideshow *text_ss = (text_slideshow *)data; if (!text_ss->transition || !text_ss->slide_time) return; if (text_ss->restart_on_activate && text_ss->use_cut) { text_ss->elapsed = 0.0f; text_ss->cur_item = text_ss->randomize ? random_text_src(text_ss) : 0; do_transition(text_ss, false); text_ss->restart_on_activate = false; text_ss->use_cut = false; text_ss->stop = false; return; } if (text_ss->pause_on_deactivate || text_ss->manual || text_ss->stop || text_ss->paused) return; /* ----------------------------------------------------- */ /* fade to transparency when the file list becomes empty */ if (!text_ss->text_srcs.num) { obs_source_t *active_transition_source = obs_transition_get_active_source(text_ss->transition); if (active_transition_source) { obs_source_release(active_transition_source); do_transition(text_ss, true); } } /* ----------------------------------------------------- */ /* do transition when slide time reached */ text_ss->elapsed += seconds; if (text_ss->elapsed > text_ss->slide_time) { text_ss->elapsed -= text_ss->slide_time; if (!text_ss->loop && text_ss->cur_item == text_ss->text_srcs.num - 1) { if (text_ss->hide) do_transition(text_ss, true); else do_transition(text_ss, false); return; } if (text_ss->randomize) { size_t next = text_ss->cur_item; if (text_ss->text_srcs.num > 1) { while (next == text_ss->cur_item) next = random_text_src(text_ss); } text_ss->cur_item = next; } else if (++text_ss->cur_item >= text_ss->text_srcs.num) { text_ss->cur_item = 0; } if (text_ss->text_srcs.num) do_transition(text_ss, false); } } static inline bool text_ss_audio_render_(obs_source_t *transition, uint64_t *ts_out, struct obs_source_audio_mix *audio_output, uint32_t mixers, size_t channels, size_t sample_rate) { struct obs_source_audio_mix child_audio; uint64_t source_ts; if (obs_source_audio_pending(transition)) return false; source_ts = obs_source_get_audio_timestamp(transition); if (!source_ts) return false; obs_source_get_audio_mix(transition, &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->output[mix].data[ch]; float *in = child_audio.output[mix].data[ch]; memcpy(out, in, AUDIO_OUTPUT_FRAMES * MAX_AUDIO_CHANNELS * sizeof(float)); } } *ts_out = source_ts; UNUSED_PARAMETER(sample_rate); return true; } bool text_ss_audio_render(void *data, uint64_t *ts_out, struct obs_source_audio_mix *audio_output, uint32_t mixers, size_t channels, size_t sample_rate) { struct text_slideshow *text_ss = (text_slideshow *)data; obs_source_t *transition = get_transition(text_ss); bool success; if (!transition) return false; success = text_ss_audio_render_(transition, ts_out, audio_output, mixers, channels, sample_rate); obs_source_release(transition); return success; } void text_ss_enum_sources(void *data, obs_source_enum_proc_t cb, void *param) { struct text_slideshow *text_ss = (text_slideshow *)data; pthread_mutex_lock(&text_ss->mutex); if (text_ss->transition) cb(text_ss->source, text_ss->transition, param); pthread_mutex_unlock(&text_ss->mutex); } uint32_t text_ss_width(void *data) { struct text_slideshow *text_ss = (text_slideshow *)data; return text_ss->transition ? text_ss->cx : 0; } uint32_t text_ss_height(void *data) { struct text_slideshow *text_ss = (text_slideshow *)data; return text_ss->transition ? text_ss->cy : 0; } void ss_defaults(obs_data_t *settings) { obs_data_set_default_string(settings, S_TRANSITION, "fade"); obs_data_set_default_int(settings, S_SLIDE_TIME, 8000); obs_data_set_default_int(settings, S_TR_SPEED, 700); obs_data_set_default_string(settings, S_CUSTOM_SIZE, T_CUSTOM_SIZE_AUTO); obs_data_set_default_string(settings, S_BEHAVIOR, S_BEHAVIOR_ALWAYS_PLAY); obs_data_set_default_string(settings, S_MODE, S_MODE_AUTO); obs_data_set_default_bool(settings, S_LOOP, true); } static const char *aspects[] = {"16:9", "16:10", "4:3", "1:1"}; #define NUM_ASPECTS (sizeof(aspects) / sizeof(const char *)) static bool use_file_changed(obs_properties_t *props, obs_property_t *p, obs_data_t *s) { bool use_single_file = obs_data_get_bool(s, S_READ_SINGLE_FILE); bool use_multiple_files = obs_data_get_bool(s, S_READ_MULTIPLE_FILES); set_vis(S_CUSTOM_DELIM, use_single_file); set_vis(S_TXT_FILE, use_single_file); set_vis(S_FILES, use_multiple_files); set_vis(S_TEXTS, !use_single_file && !use_multiple_files); return true; } static bool use_custom_delim_changed(obs_properties_t *props, obs_property_t *p, obs_data_t *s) { bool use_custom_delim = obs_data_get_bool(s, S_CUSTOM_DELIM); set_vis(S_DELIM, use_custom_delim); return true; } void ss_properites(void *data, obs_properties_t *props) { struct text_slideshow *text_ss = (text_slideshow *)data; struct obs_video_info ovi; obs_property_t *p; int cx; int cy; string path; /* ----------------- */ obs_get_video_info(&ovi); cx = (int)ovi.base_width; cy = (int)ovi.base_height; /* ----------------- */ p = obs_properties_add_bool(props, S_READ_SINGLE_FILE, T_USE_SINGLE_FILE); obs_property_set_modified_callback(p, use_file_changed); string filter; filter += T_FILTER_TEXT_FILES; filter += " (*.txt);;"; filter += T_FILTER_ALL_FILES; filter += " (*.*)"; if (text_ss && !text_ss->file.empty()) { const char *slash; path = text_ss->file; replace(path.begin(), path.end(), '\\', '/'); slash = strrchr(path.c_str(), '/'); if (slash) path.resize(slash - path.c_str() + 1); } p = obs_properties_add_bool(props, S_CUSTOM_DELIM, T_USE_CUSTOM_DELIM); obs_property_set_modified_callback(p, use_custom_delim_changed); obs_properties_add_text(props, S_DELIM, T_CUSTOM_DELIM, OBS_TEXT_DEFAULT); obs_properties_add_path(props, S_TXT_FILE, T_FILE, OBS_PATH_FILE, filter.c_str(), path.c_str()); p = obs_properties_add_bool(props, S_READ_MULTIPLE_FILES, T_USE_MULTIPLE_FILE); obs_property_set_modified_callback(p, use_file_changed); obs_properties_add_editable_list(props, S_FILES, T_FILES, OBS_EDITABLE_LIST_TYPE_FILES, NULL, NULL); obs_properties_add_editable_list(props, S_TEXTS, T_TEXTS, OBS_EDITABLE_LIST_TYPE_STRINGS, NULL, NULL); p = obs_properties_add_list(props, S_BEHAVIOR, T_BEHAVIOR, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); obs_property_list_add_string(p, T_BEHAVIOR_ALWAYS_PLAY, S_BEHAVIOR_ALWAYS_PLAY); obs_property_list_add_string(p, T_BEHAVIOR_STOP_RESTART, S_BEHAVIOR_STOP_RESTART); obs_property_list_add_string(p, T_BEHAVIOR_PAUSE_UNPAUSE, S_BEHAVIOR_PAUSE_UNPAUSE); p = obs_properties_add_list(props, S_MODE, T_MODE, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); obs_property_list_add_string(p, T_MODE_AUTO, S_MODE_AUTO); obs_property_list_add_string(p, T_MODE_MANUAL, S_MODE_MANUAL); p = obs_properties_add_list(props, S_TRANSITION, T_TRANSITION, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); obs_property_list_add_string(p, T_TR_CUT, TR_CUT); obs_property_list_add_string(p, T_TR_FADE, TR_FADE); obs_property_list_add_string(p, T_TR_SWIPE, TR_SWIPE); obs_property_list_add_string(p, T_TR_SLIDE, TR_SLIDE); obs_properties_add_int(props, S_SLIDE_TIME, T_SLIDE_TIME, 50, 3600000, 50); obs_properties_add_int(props, S_TR_SPEED, T_TR_SPEED, 0, 3600000, 50); obs_properties_add_bool(props, S_LOOP, T_LOOP); obs_properties_add_bool(props, S_HIDE, T_HIDE); obs_properties_add_bool(props, S_RANDOMIZE, T_RANDOMIZE); p = obs_properties_add_list(props, S_CUSTOM_SIZE, T_CUSTOM_SIZE, OBS_COMBO_TYPE_EDITABLE, OBS_COMBO_FORMAT_STRING); obs_property_list_add_string(p, T_CUSTOM_SIZE_AUTO, T_CUSTOM_SIZE_AUTO); for (size_t i = 0; i < NUM_ASPECTS; i++) obs_property_list_add_string(p, aspects[i], aspects[i]); char str[32]; snprintf(str, 32, "%dx%d", cx, cy); obs_property_list_add_string(p, str, str); } void text_ss_play_pause(void *data, bool pause) { struct text_slideshow *text_ss = (text_slideshow *)data; if (text_ss->stop) { text_ss->stop = false; text_ss->paused = false; do_transition(text_ss, false); } else { text_ss->paused = pause; text_ss->manual = pause; } if (pause) { set_media_state(text_ss, OBS_MEDIA_STATE_PAUSED); obs_data_set_string(text_ss->settings, S_MODE, S_MODE_MANUAL); } else { set_media_state(text_ss, OBS_MEDIA_STATE_PLAYING); obs_data_set_string(text_ss->settings, S_MODE, S_MODE_AUTO); } } void text_ss_restart(void *data) { struct text_slideshow *text_ss = (text_slideshow *)data; text_ss->elapsed = 0.0f; text_ss->cur_item = 0; text_ss->stop = false; text_ss->paused = false; do_transition(text_ss, false); set_media_state(text_ss, OBS_MEDIA_STATE_PLAYING); obs_data_set_string(text_ss->settings, S_MODE, S_MODE_AUTO); } void text_ss_stop(void *data) { struct text_slideshow *text_ss = (text_slideshow *)data; text_ss->elapsed = 0.0f; text_ss->cur_item = 0; do_transition(text_ss, true); text_ss->stop = true; text_ss->paused = false; set_media_state(text_ss, OBS_MEDIA_STATE_STOPPED); obs_data_set_string(text_ss->settings, S_MODE, S_MODE_MANUAL); } void text_ss_next_slide(void *data) { struct text_slideshow *text_ss = (text_slideshow *)data; if (!text_ss->text_srcs.num || obs_transition_get_time(text_ss->transition) < 1.0f) return; if (++text_ss->cur_item >= text_ss->text_srcs.num) text_ss->cur_item = 0; do_transition(text_ss, false); } void text_ss_previous_slide(void *data) { struct text_slideshow *text_ss = (text_slideshow *)data; if (!text_ss->text_srcs.num || obs_transition_get_time(text_ss->transition) < 1.0f) return; if (text_ss->cur_item == 0) text_ss->cur_item = text_ss->text_srcs.num - 1; else --text_ss->cur_item; do_transition(text_ss, false); } enum obs_media_state text_ss_get_state(void *data) { struct text_slideshow *text_ss = (text_slideshow *)data; return text_ss->state; } obs-text-slideshow-1.5.2/src/obs-text-slideshow.h000066400000000000000000000140541415025745200220000ustar00rootroot00000000000000/* obs-text-slideshow Copyright (C) 2021 Joshua Wong jbwong05@gmail.com 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, see */ #pragma once #include "obs-module.h" #include #include #include #include #include #include using std::string; using std::vector; #define S_TR_SPEED "transition_speed" #define S_CUSTOM_SIZE "use_custom_size" #define S_SLIDE_TIME "slide_time" #define S_TRANSITION "transition" #define S_RANDOMIZE "randomize" #define S_LOOP "loop" #define S_HIDE "hide" #define S_TEXTS "texts" #define S_BEHAVIOR "playback_behavior" #define S_BEHAVIOR_STOP_RESTART "stop_restart" #define S_BEHAVIOR_PAUSE_UNPAUSE "pause_unpause" #define S_BEHAVIOR_ALWAYS_PLAY "always_play" #define S_MODE "slide_mode" #define S_MODE_AUTO "mode_auto" #define S_MODE_MANUAL "mode_manual" #define S_READ_SINGLE_FILE "read_single_file" #define S_CUSTOM_DELIM "custom_delim" #define S_DELIM "delim" #define S_TXT_FILE "txt_file" #define S_READ_MULTIPLE_FILES "read_multiple_files" #define S_FILES "files" #define TR_CUT "cut" #define TR_FADE "fade" #define TR_SWIPE "swipe" #define TR_SLIDE "slide" #define T_SS_(text) obs_module_text("SlideShow." text) #define T_TR_SPEED T_SS_("TransitionSpeed") #define T_CUSTOM_SIZE T_SS_("CustomSize") #define T_CUSTOM_SIZE_AUTO T_SS_("CustomSize.Auto") #define T_SLIDE_TIME T_SS_("SlideTime") #define T_TRANSITION T_SS_("Transition") #define T_RANDOMIZE T_SS_("Randomize") #define T_LOOP T_SS_("Loop") #define T_HIDE T_SS_("HideWhenDone") #define T_TEXTS T_SS_("Texts") #define T_BEHAVIOR T_SS_("PlaybackBehavior") #define T_BEHAVIOR_STOP_RESTART T_SS_("PlaybackBehavior.StopRestart") #define T_BEHAVIOR_PAUSE_UNPAUSE T_SS_("PlaybackBehavior.PauseUnpause") #define T_BEHAVIOR_ALWAYS_PLAY T_SS_("PlaybackBehavior.AlwaysPlay") #define T_MODE T_SS_("SlideMode") #define T_MODE_AUTO T_SS_("SlideMode.Auto") #define T_MODE_MANUAL T_SS_("SlideMode.Manual") #define T_USE_SINGLE_FILE T_SS_("ReadFromSingleFile") #define T_USE_CUSTOM_DELIM T_SS_("UseCustomDelim") #define T_CUSTOM_DELIM T_SS_("CustomDelim") #define T_FILE T_SS_("TextFile") #define T_FILTER_TEXT_FILES T_SS_("Filter.TextFiles") #define T_FILTER_ALL_FILES T_SS_("Filter.AllFiles") #define T_USE_MULTIPLE_FILE T_SS_("ReadFromMultipleFiles") #define T_FILES T_SS_("Files") #define T_TR_(text) obs_module_text("SlideShow.Transition." text) #define T_TR_CUT T_TR_("Cut") #define T_TR_FADE T_TR_("Fade") #define T_TR_SWIPE T_TR_("Swipe") #define T_TR_SLIDE T_TR_("Slide") #define set_vis(val, show) \ do { \ p = obs_properties_get(props, val); \ obs_property_set_visible(p, show); \ } while (false) struct text_data { char *file_path; char *text; obs_source_t *source; }; enum behavior { BEHAVIOR_STOP_RESTART, BEHAVIOR_PAUSE_UNPAUSE, BEHAVIOR_ALWAYS_PLAY, }; struct text_slideshow { obs_source_t *source; obs_data_t *settings; bool randomize; bool loop; bool restart_on_activate; bool pause_on_deactivate; bool restart; bool manual; bool hide; bool use_cut; bool paused; bool stop; float slide_time; uint32_t tr_speed; const char *tr_name; obs_source_t *transition; float elapsed; size_t cur_item; uint32_t cx; uint32_t cy; bool dock_can_get_texts; pthread_cond_t dock_get_texts; pthread_mutex_t mutex; DARRAY(struct text_data) text_srcs; bool read_from_single_file = false; bool read_from_multiple_files = false; string file; const char *custom_delim = NULL; enum behavior behavior; obs_hotkey_id play_pause_hotkey; obs_hotkey_id restart_hotkey; obs_hotkey_id stop_hotkey; obs_hotkey_id next_hotkey; obs_hotkey_id prev_hotkey; enum obs_media_state state; }; typedef obs_source_t *(*text_source_create)(const char *path, const char *text, obs_data_t *text_ss_settings); typedef void (*set_text_alignment)(obs_source_t *transition, obs_data_t *text_ss_settings); void play_pause_hotkey(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey, bool pressed); void restart_hotkey(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey, bool pressed); void stop_hotkey(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey, bool pressed); void next_slide_hotkey(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey, bool pressed); void previous_slide_hotkey(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey, bool pressed); void text_ss_destroy(void *data); void *text_ss_create(obs_data_t *settings, obs_source_t *source); void text_ss_update(void *data, obs_data_t *settings, text_source_create text_creator, set_text_alignment set_alignment); void text_ss_activate(void *data); void text_ss_deactivate(void *data); void text_ss_video_render(void *data, gs_effect_t *effect); void text_ss_video_tick(void *data, float seconds); bool text_ss_audio_render(void *data, uint64_t *ts_out, struct obs_source_audio_mix *audio_output, uint32_t mixers, size_t channels, size_t sample_rate); void text_ss_enum_sources(void *data, obs_source_enum_proc_t cb, void *param); void text_ss_enum_all_sources(void *data, obs_source_enum_proc_t callback, void *param); uint32_t text_ss_width(void *data); uint32_t text_ss_height(void *data); void ss_defaults(obs_data_t *settings); void ss_properites(void *data, obs_properties_t *props); void text_ss_play_pause(void *data, bool pause); void text_ss_restart(void *data); void text_ss_stop(void *data); void text_ss_next_slide(void *data); void text_ss_previous_slide(void *data); enum obs_media_state text_ss_get_state(void *data);obs-text-slideshow-1.5.2/src/plugin-macros.h.in000066400000000000000000000016421415025745200214200ustar00rootroot00000000000000/* obs-text-slideshow Copyright (C) 2021 Joshua Wong jbwong05@gmail.com 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, see */ #ifndef PLUGINNAME_H #define PLUGINNAME_H #define PLUGIN_NAME "@CMAKE_PROJECT_NAME@" #define PLUGIN_VERSION "@CMAKE_PROJECT_VERSION@" #define blog(level, msg, ...) blog(level, "[" PLUGIN_NAME "] " msg, ##__VA_ARGS__) #endif // PLUGINNAME_Hobs-text-slideshow-1.5.2/src/plugin-main.cpp000066400000000000000000000030371415025745200210060ustar00rootroot00000000000000/* obs-text-slideshow Copyright (C) 2021 Joshua Wong jbwong05@gmail.com 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, see */ #include #include #include #include "plugin-macros.generated.h" #include "obs-text-slideshow-dock.h" OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE(PLUGIN_NAME, "en-US") extern void load_text_freetype2_slideshow(); #ifdef _WIN32 extern void load_text_gdiplus_slideshow(); #endif bool obs_module_load(void) { load_text_freetype2_slideshow(); #ifdef _WIN32 load_text_gdiplus_slideshow(); #endif const auto main_window = static_cast(obs_frontend_get_main_window()); obs_frontend_push_ui_translation(obs_module_get_string); auto *tmp = new TextSlideshowDock(main_window); obs_frontend_add_dock(tmp); obs_frontend_pop_ui_translation(); blog(LOG_INFO, "plugin loaded successfully (version %s)", PLUGIN_VERSION); return true; } void obs_module_unload() { blog(LOG_INFO, "plugin unloaded"); }