├── .clang-format ├── .gitattributes ├── .github └── FUNDING.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── _config.yml ├── appveyor.yml ├── cmake_build.cmd ├── docs ├── Examples.rst ├── License.rst ├── Makefile ├── Requirements.rst ├── UserAPI.rst ├── _static │ └── custom.css ├── conf.py ├── index.rst ├── make.bat └── requirements.txt ├── inc └── logger │ ├── async_logger.h │ ├── common.h │ ├── contrib │ ├── README.md │ └── sinks │ │ ├── .gitignore │ │ └── step_file_sink.h │ ├── details │ ├── async_log_helper.h │ ├── async_logger_impl.h │ ├── file_helper.h │ ├── log_msg.h │ ├── logger_impl.h │ ├── mpmc_blocking_q.h │ ├── null_mutex.h │ ├── os.h │ ├── pattern_formatter_impl.h │ ├── registry.h │ └── spdlog_impl.h │ ├── fmt │ ├── bundled │ │ ├── LICENSE.rst │ │ ├── format.cc │ │ ├── format.h │ │ ├── ostream.cc │ │ ├── ostream.h │ │ ├── posix.cc │ │ ├── posix.h │ │ ├── printf.cc │ │ ├── printf.h │ │ └── time.h │ ├── fmt.h │ └── ostr.h │ ├── formatter.h │ ├── logger.h │ ├── sinks │ ├── android_sink.h │ ├── ansicolor_sink.h │ ├── base_sink.h │ ├── dist_sink.h │ ├── file_sinks.h │ ├── msvc_sink.h │ ├── null_sink.h │ ├── ostream_sink.h │ ├── sink.h │ ├── stdout_sinks.h │ ├── syslog_sink.h │ ├── wincolor_sink.h │ └── windebug_sink.h │ ├── spdlog.h │ └── tweakme.h ├── java └── screen_recorder │ ├── pom.xml │ ├── settings.xml │ ├── src │ └── main │ │ ├── java │ │ ├── module-info.java │ │ └── screen_recorder │ │ │ ├── ExitCode.java │ │ │ ├── RecorderError.java │ │ │ ├── RecorderParams.java │ │ │ └── ScreenRecorder.java │ │ └── resources │ │ └── ScreenRecorder.dll │ └── target │ ├── screen_recorder-jar-with-dependencies.jar │ └── screen_recorder.jar ├── python ├── README.md ├── example │ ├── recorder_simple_sample.py │ └── recorder_threaded_sample.py ├── screen_recorder_sdk │ ├── __init__.py │ ├── exit_codes.py │ ├── lib │ │ └── ScreenRecorder.dll │ └── screen_recorder.py └── setup.py └── src ├── ScreenRecorder ├── MFEncoder.cpp ├── Recorder.cpp ├── RecorderDDA.cpp ├── ScreenRecorder.cpp └── inc │ ├── MFEncoder.h │ ├── Recorder.h │ ├── RecorderDDA.h │ ├── RecorderParams.h │ ├── ScreenRecorder.h │ └── json.hpp └── Utils ├── HandleD3DWindow.cpp └── inc └── HandleD3DWindow.h /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: WebKit 3 | AccessModifierOffset: '-4' 4 | AlignEscapedNewlinesLeft: 'false' 5 | AlignTrailingComments: 'true' 6 | AllowAllParametersOfDeclarationOnNextLine: 'false' 7 | AllowShortBlocksOnASingleLine: 'false' 8 | AllowShortFunctionsOnASingleLine: None 9 | AllowShortIfStatementsOnASingleLine: 'false' 10 | AllowShortLoopsOnASingleLine: 'false' 11 | AlwaysBreakBeforeMultilineStrings: 'false' 12 | AlwaysBreakTemplateDeclarations: 'false' 13 | BinPackParameters: 'true' 14 | BreakBeforeBinaryOperators: 'false' 15 | BreakBeforeBraces: Allman 16 | BreakBeforeTernaryOperators: 'false' 17 | BreakConstructorInitializersBeforeComma: 'false' 18 | ColumnLimit: '100' 19 | ConstructorInitializerAllOnOneLineOrOnePerLine: 'true' 20 | DisableFormat: 'false' 21 | IndentCaseLabels: 'true' 22 | IndentWidth: '4' 23 | KeepEmptyLinesAtTheStartOfBlocks: 'true' 24 | Language: Cpp 25 | MaxEmptyLinesToKeep: '2' 26 | NamespaceIndentation: All 27 | PointerAlignment: Right 28 | SpaceBeforeAssignmentOperators: 'true' 29 | SpaceBeforeParens: Always 30 | SpaceInEmptyParentheses: 'false' 31 | SpacesInAngles: 'false' 32 | SpacesInCStyleCastParentheses: 'false' 33 | SpacesInContainerLiterals: 'false' 34 | SpacesInParentheses: 'false' 35 | Standard: Cpp11 36 | TabWidth: '4' 37 | UseTab: Never 38 | Cpp11BracedListStyle: 'true' 39 | 40 | ... 41 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.a filter=lfs diff=lfs merge=lfs -text 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: Andrey1994 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | 56 | # StyleCop 57 | StyleCopReport.xml 58 | 59 | # Files built by Visual Studio 60 | *_i.c 61 | *_p.c 62 | *_i.h 63 | *.ilk 64 | *.meta 65 | *.obj 66 | *.iobj 67 | *.pch 68 | *.pdb 69 | *.ipdb 70 | *.pgc 71 | *.pgd 72 | *.rsp 73 | *.sbr 74 | *.tlb 75 | *.tli 76 | *.tlh 77 | *.tmp 78 | *.tmp_proj 79 | *.log 80 | *.vspscc 81 | *.vssscc 82 | .builds 83 | *.pidb 84 | *.svclog 85 | *.scc 86 | 87 | # Chutzpah Test files 88 | _Chutzpah* 89 | 90 | # Visual C++ cache files 91 | ipch/ 92 | *.aps 93 | *.ncb 94 | *.opendb 95 | *.opensdf 96 | *.sdf 97 | *.cachefile 98 | *.VC.db 99 | *.VC.VC.opendb 100 | 101 | # Visual Studio profiler 102 | *.psess 103 | *.vsp 104 | *.vspx 105 | *.sap 106 | 107 | # Visual Studio Trace Files 108 | *.e2e 109 | 110 | # TFS 2012 Local Workspace 111 | $tf/ 112 | 113 | # Guidance Automation Toolkit 114 | *.gpState 115 | 116 | # ReSharper is a .NET coding add-in 117 | _ReSharper*/ 118 | *.[Rr]e[Ss]harper 119 | *.DotSettings.user 120 | 121 | # JustCode is a .NET coding add-in 122 | .JustCode 123 | 124 | # TeamCity is a build add-in 125 | _TeamCity* 126 | 127 | # DotCover is a Code Coverage Tool 128 | *.dotCover 129 | 130 | # AxoCover is a Code Coverage Tool 131 | .axoCover/* 132 | !.axoCover/settings.json 133 | 134 | # Visual Studio code coverage results 135 | *.coverage 136 | *.coveragexml 137 | 138 | # NCrunch 139 | _NCrunch_* 140 | .*crunch*.local.xml 141 | nCrunchTemp_* 142 | 143 | # MightyMoose 144 | *.mm.* 145 | AutoTest.Net/ 146 | 147 | # Web workbench (sass) 148 | .sass-cache/ 149 | 150 | # Installshield output folder 151 | [Ee]xpress/ 152 | 153 | # DocProject is a documentation generator add-in 154 | DocProject/buildhelp/ 155 | DocProject/Help/*.HxT 156 | DocProject/Help/*.HxC 157 | DocProject/Help/*.hhc 158 | DocProject/Help/*.hhk 159 | DocProject/Help/*.hhp 160 | DocProject/Help/Html2 161 | DocProject/Help/html 162 | 163 | # Click-Once directory 164 | publish/ 165 | 166 | # Publish Web Output 167 | *.[Pp]ublish.xml 168 | *.azurePubxml 169 | # Note: Comment the next line if you want to checkin your web deploy settings, 170 | # but database connection strings (with potential passwords) will be unencrypted 171 | *.pubxml 172 | *.publishproj 173 | 174 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 175 | # checkin your Azure Web App publish settings, but sensitive information contained 176 | # in these scripts will be unencrypted 177 | PublishScripts/ 178 | 179 | # NuGet Packages 180 | *.nupkg 181 | # The packages folder can be ignored because of Package Restore 182 | **/[Pp]ackages/* 183 | # except build/, which is used as an MSBuild target. 184 | !**/[Pp]ackages/build/ 185 | # Uncomment if necessary however generally it will be regenerated when needed 186 | #!**/[Pp]ackages/repositories.config 187 | # NuGet v3's project.json files produces more ignorable files 188 | *.nuget.props 189 | *.nuget.targets 190 | 191 | # Microsoft Azure Build Output 192 | csx/ 193 | *.build.csdef 194 | 195 | # Microsoft Azure Emulator 196 | ecf/ 197 | rcf/ 198 | 199 | # Windows Store app package directories and files 200 | AppPackages/ 201 | BundleArtifacts/ 202 | Package.StoreAssociation.xml 203 | _pkginfo.txt 204 | *.appx 205 | 206 | # Visual Studio cache files 207 | # files ending in .cache can be ignored 208 | *.[Cc]ache 209 | # but keep track of directories ending in .cache 210 | !*.[Cc]ache/ 211 | 212 | # Others 213 | ClientBin/ 214 | ~$* 215 | *~ 216 | *.dbmdl 217 | *.dbproj.schemaview 218 | *.jfm 219 | *.pfx 220 | *.publishsettings 221 | orleans.codegen.cs 222 | 223 | # Including strong name files can present a security risk 224 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 225 | #*.snk 226 | 227 | # Since there are multiple workflows, uncomment next line to ignore bower_components 228 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 229 | #bower_components/ 230 | 231 | # RIA/Silverlight projects 232 | Generated_Code/ 233 | 234 | # Backup & report files from converting an old project file 235 | # to a newer Visual Studio version. Backup files are not needed, 236 | # because we have git ;-) 237 | _UpgradeReport_Files/ 238 | Backup*/ 239 | UpgradeLog*.XML 240 | UpgradeLog*.htm 241 | ServiceFabricBackup/ 242 | *.rptproj.bak 243 | 244 | # SQL Server files 245 | *.mdf 246 | *.ldf 247 | *.ndf 248 | 249 | # Business Intelligence projects 250 | *.rdl.data 251 | *.bim.layout 252 | *.bim_*.settings 253 | *.rptproj.rsuser 254 | 255 | # Microsoft Fakes 256 | FakesAssemblies/ 257 | 258 | # GhostDoc plugin setting file 259 | *.GhostDoc.xml 260 | 261 | # Node.js Tools for Visual Studio 262 | .ntvs_analysis.dat 263 | node_modules/ 264 | 265 | # Visual Studio 6 build log 266 | *.plg 267 | 268 | # Visual Studio 6 workspace options file 269 | *.opt 270 | 271 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 272 | *.vbw 273 | 274 | # Visual Studio LightSwitch build output 275 | **/*.HTMLClient/GeneratedArtifacts 276 | **/*.DesktopClient/GeneratedArtifacts 277 | **/*.DesktopClient/ModelManifest.xml 278 | **/*.Server/GeneratedArtifacts 279 | **/*.Server/ModelManifest.xml 280 | _Pvt_Extensions 281 | 282 | # Paket dependency manager 283 | .paket/paket.exe 284 | paket-files/ 285 | 286 | # FAKE - F# Make 287 | .fake/ 288 | 289 | # JetBrains Rider 290 | .idea/ 291 | *.sln.iml 292 | 293 | # CodeRush 294 | .cr/ 295 | 296 | # Python Tools for Visual Studio (PTVS) 297 | __pycache__/ 298 | *.pyc 299 | 300 | # Cake - Uncomment if you are using it 301 | # tools/** 302 | # !tools/packages.config 303 | 304 | # Tabs Studio 305 | *.tss 306 | 307 | # Telerik's JustMock configuration file 308 | *.jmconfig 309 | 310 | # BizTalk build output 311 | *.btp.cs 312 | *.btm.cs 313 | *.odx.cs 314 | *.xsd.cs 315 | 316 | # OpenCover UI analysis results 317 | OpenCover/ 318 | 319 | # Azure Stream Analytics local run output 320 | ASALocalRun/ 321 | 322 | # MSBuild Binary and Structured Log 323 | *.binlog 324 | 325 | # NVidia Nsight GPU debugger configuration file 326 | *.nvuser 327 | 328 | # MFractors (Xamarin productivity tool) working folder 329 | .mfractor/ 330 | 331 | .exe 332 | .raw 333 | .txt 334 | 335 | build/ 336 | python/flowcat.egg-info/ 337 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.12) 2 | project (screen_recorder_sdk) 3 | 4 | macro(configure_msvc_runtime) 5 | if(MSVC) 6 | # Default to statically-linked runtime. 7 | if("${MSVC_RUNTIME}" STREQUAL "") 8 | set(MSVC_RUNTIME "static") 9 | endif() 10 | # Set compiler options. 11 | set(variables 12 | CMAKE_C_FLAGS_DEBUG 13 | CMAKE_C_FLAGS_MINSIZEREL 14 | CMAKE_C_FLAGS_RELEASE 15 | CMAKE_C_FLAGS_RELWITHDEBINFO 16 | CMAKE_CXX_FLAGS_DEBUG 17 | CMAKE_CXX_FLAGS_MINSIZEREL 18 | CMAKE_CXX_FLAGS_RELEASE 19 | CMAKE_CXX_FLAGS_RELWITHDEBINFO 20 | ) 21 | if(${MSVC_RUNTIME} STREQUAL "static") 22 | message(STATUS 23 | "MSVC -> forcing use of statically-linked runtime." 24 | ) 25 | foreach(variable ${variables}) 26 | if(${variable} MATCHES "/MD") 27 | string(REGEX REPLACE "/MD" "/MT" ${variable} "${${variable}}") 28 | endif() 29 | endforeach() 30 | else() 31 | message(STATUS 32 | "MSVC -> forcing use of dynamically-linked runtime." 33 | ) 34 | foreach(variable ${variables}) 35 | if(${variable} MATCHES "/MT") 36 | string(REGEX REPLACE "/MT" "/MD" ${variable} "${${variable}}") 37 | endif() 38 | endforeach() 39 | endif() 40 | endif() 41 | endmacro() 42 | 43 | configure_msvc_runtime () 44 | 45 | include_directories ( 46 | inc 47 | src/Utils/inc 48 | src/ScreenRecorder/inc 49 | ) 50 | 51 | set (CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_SOURCE_DIR}/compiled) 52 | set (CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_SOURCE_DIR}/compiled) 53 | 54 | add_library ( 55 | ScreenRecorder SHARED 56 | src/ScreenRecorder/ScreenRecorder.cpp 57 | src/ScreenRecorder/Recorder.cpp 58 | src/ScreenRecorder/RecorderDDA.cpp 59 | src/ScreenRecorder/MFEncoder.cpp 60 | src/Utils/HandleD3DWindow.cpp 61 | ) 62 | 63 | set (CMAKE_SHARED_LINKER_FLAGS "dwmapi.lib") -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Andrey Parfenov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Screen Recorder SDK 2 | Library to take screenshots and record videos 3 | 4 | We use [Desktop Duplication API](https://docs.microsoft.com/en-us/windows/desktop/direct3ddxgi/desktop-dup-api) to capture desktop and [Media Foundation API](https://docs.microsoft.com/en-us/windows/desktop/medfound/media-foundation-platform-apis) to record video. 5 | 6 | ## System Requirements 7 | 8 | * Windows >= 10, it may work on Windows 8.1 and Windows Server 2012, but we don't ensure it 9 | * DirectX, you can install it from [Microsoft Website](https://www.microsoft.com/en-us/download/details.aspx?id=17431) 10 | * Media Feature Pack, download it [here](https://www.microsoft.com/en-us/software-download/mediafeaturepack) 11 | * 64 bits Java or Python, we don't provide x86 libs 12 | 13 | ## Build Status 14 | *Windows(AppVeyor) 15 | [![Build status](https://ci.appveyor.com/api/projects/status/3wnq5b1ipackukbc/branch/master?svg=true)](https://ci.appveyor.com/project/Andrey1994/screen-recorder-sdk/branch/master) 16 | 17 | 18 | ## [Docs and Code Samples](https://screen-recorder-sdk.readthedocs.io/en/latest/) 19 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | PY_DIR: C:\Python36-x64 3 | PYPI_PASSWORD: 4 | secure: ZuRCW1W45pqH03Lio54BzQ== 5 | GITHUB_TOKEN: 6 | secure: Q0/Pyd8J+EX6/zaOuWATGu2OvRBLz53SYEFejI91VFiPEOJ0KbyZl+2UdYSzniDI 7 | 8 | skip_non_tags: false 9 | 10 | image: 11 | - Visual Studio 2019 12 | 13 | platform: 14 | - x64 15 | 16 | stack: python 3 17 | 18 | clone_depth: 3 19 | 20 | build: off 21 | 22 | init: 23 | - cmd: set PATH=C:\Program Files\Java\jdk11\bin;%PY_DIR%;%PY_DIR%\Scripts;%APPVEYOR_BUILD_FOLDER%\data\lib;%PATH% 24 | - cmd: set CNC_INSTALL_CNCA0_CNCB0_PORTS="YES" 25 | - cmd: set SCREEN_RECORDER_SDK_VERSION=%APPVEYOR_REPO_TAG_NAME% 26 | - cmd: set JAVA_HOME=C:\Program Files\Java\jdk11 27 | 28 | install: 29 | - pip install twine 30 | - pip install numpy==1.18.4 31 | 32 | test_script: 33 | - python python\setup.py install 34 | - cd java\screen_recorder && mvn package 35 | 36 | after_test: 37 | - pip install wheel 38 | - cd python && python setup.py sdist bdist_wheel 39 | 40 | artifacts: 41 | - path: dist 42 | name: python_package 43 | type: zip 44 | 45 | deploy_script: 46 | - ps: >- 47 | If ($env:APPVEYOR_REPO_TAG -eq "true" -And $env:APPVEYOR_REPO_BRANCH -eq "master") { 48 | cd $env:APPVEYOR_BUILD_FOLDER\python 49 | twine upload --skip-existing dist/*.whl --user Andrey1994 --password $env:PYPI_PASSWORD 50 | cd $env:APPVEYOR_BUILD_FOLDER\java\screen_recorder 51 | (gc .\pom.xml).replace('0.0.1-SNAPSHOT',$env:APPVEYOR_REPO_TAG_NAME) | Out-File -encoding ASCII pom.xml 52 | (gc .\settings.xml).replace('USER','Andrey1994') | Out-File -encoding ASCII settings.xml 53 | (gc .\settings.xml).replace('TOKEN',$env:GITHUB_TOKEN) | Out-File -encoding ASCII settings.xml 54 | mvn -s $env:APPVEYOR_BUILD_FOLDER\java\screen_recorder\settings.xml deploy -Dregistry=https://maven.pkg.github.com/Andrey1994 -Dtoken=$env:GITHUB_TOKEN 55 | } 56 | Else { 57 | write-output "Skip deployment for non tag" 58 | } 59 | 60 | notifications: 61 | - provider: Email 62 | to: 63 | - a1994ndrey@gmail.com 64 | on_build_success: false 65 | on_build_failure: true 66 | on_build_status_changed: true 67 | -------------------------------------------------------------------------------- /cmake_build.cmd: -------------------------------------------------------------------------------- 1 | rmdir /S /Q build 2 | mkdir build 3 | 4 | cd build 5 | 6 | cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_SYSTEM_VERSION=10.0.15063.0 .. 7 | cmake --build . --config Release 8 | cd .. 9 | 10 | xcopy compiled\ScreenRecorder.dll python\screen_recorder_sdk\lib\ /s /Y 11 | xcopy compiled\ScreenRecorder.dll java\screen_recorder\src\main\resources\ /s /Y -------------------------------------------------------------------------------- /docs/Examples.rst: -------------------------------------------------------------------------------- 1 | .. _code-samples-label: 2 | 3 | Code Samples 4 | ============= 5 | 6 | Python 7 | -------- 8 | 9 | Python Basic Demo 10 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 11 | 12 | .. literalinclude:: ../python/example/recorder_simple_sample.py 13 | :language: py 14 | -------------------------------------------------------------------------------- /docs/License.rst: -------------------------------------------------------------------------------- 1 | MIT License 2 | =========== 3 | 4 | 5 | Copyright (c) 2018 Andrey Parfenov 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SOURCEDIR = . 8 | BUILDDIR = _build 9 | 10 | # Put it first so that "make" without argument is like "make help". 11 | help: 12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 13 | 14 | .PHONY: help Makefile 15 | 16 | # Catch-all target: route all unknown targets to Sphinx using the new 17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 18 | %: Makefile 19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/Requirements.rst: -------------------------------------------------------------------------------- 1 | System Requirements 2 | ===================== 3 | 4 | - Windows >= 10, may work on Windows 8.1 and Windows Server 2012, but we don't ensure it 5 | - DirectX, you can install it from `Microsoft Website `_ 6 | - Media Feature Pack, download it `here `_ 7 | - 64 bits Java or Python, we don't provide x86 libs 8 | -------------------------------------------------------------------------------- /docs/UserAPI.rst: -------------------------------------------------------------------------------- 1 | User API 2 | ========== 3 | 4 | Python API Reference 5 | ---------------------- 6 | 7 | screen_recorder_sdk.screen\_recorder 8 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 9 | 10 | .. automodule:: screen_recorder_sdk.screen_recorder 11 | :members: 12 | :noindex: 13 | :show-inheritance: 14 | :member-order: bysource 15 | 16 | screen_recorder_sdk.exit\_codes 17 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 18 | 19 | .. automodule:: screen_recorder_sdk.exit_codes 20 | :members: 21 | :undoc-members: 22 | :noindex: 23 | :show-inheritance: 24 | :member-order: bysource 25 | -------------------------------------------------------------------------------- /docs/_static/custom.css: -------------------------------------------------------------------------------- 1 | /* Use white for logo background. color of brain on the image is de923f */ 2 | .wy-side-nav-search { 3 | background-color: #fff; 4 | } 5 | 6 | .wy-side-nav-search > div.version { 7 | color: #000; 8 | } 9 | 10 | .wy-nav-content { 11 | max-width: 100%; 12 | position: relative; 13 | overflow: auto; 14 | background: #fff; 15 | } -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Configuration file for the Sphinx documentation builder. 4 | # 5 | # This file does only contain a selection of the most common options. For a 6 | # full list see the documentation: 7 | # http://www.sphinx-doc.org/en/master/config 8 | 9 | # -- Path setup -------------------------------------------------------------- 10 | 11 | # If extensions (or modules to document with autodoc) are in another directory, 12 | # add these directories to sys.path here. If the directory is relative to the 13 | # documentation root, use os.path.abspath to make it absolute, like shown here. 14 | # 15 | import os 16 | import sys 17 | import subprocess 18 | 19 | sys.path.insert(0, os.path.abspath('.')) 20 | sys.path.insert(0, os.path.abspath(os.path.join('..', 'python'))) 21 | sys.path.insert(0, os.path.abspath(os.path.join('..', 'python', 'screen_recorder_sdk'))) 22 | 23 | # -- Project information ----------------------------------------------------- 24 | 25 | project = u'Screen Recorder SDK' 26 | copyright = u'2019, Andrey Parfenov' 27 | author = u'Andrey Parfenov' 28 | 29 | # The short X.Y version 30 | version = u'' 31 | # The full version, including alpha/beta/rc tags 32 | release = u'' 33 | 34 | 35 | # -- General configuration --------------------------------------------------- 36 | 37 | # If your documentation needs a minimal Sphinx version, state it here. 38 | # 39 | # needs_sphinx = '1.0' 40 | 41 | # Add any Sphinx extension module names here, as strings. They can be 42 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 43 | # ones. 44 | extensions = [ 45 | 'sphinx.ext.autodoc', 46 | 'sphinxcontrib.ghcontributors', 47 | 'nbsphinx', 48 | 'sphinx.ext.mathjax' 49 | ] 50 | 51 | # sphinx.ext.autodoc 52 | autoclass_content = 'both' 53 | autodoc_default_options = {'members': None} 54 | autodoc_member_order = 'bysource' 55 | 56 | 57 | # Add any paths that contain templates here, relative to this directory. 58 | templates_path = ['_templates'] 59 | 60 | # The suffix(es) of source filenames. 61 | # You can specify multiple suffix as a list of string: 62 | # 63 | # source_suffix = ['.rst', '.md'] 64 | source_suffix = '.rst' 65 | 66 | # The master toctree document. 67 | master_doc = 'index' 68 | 69 | # The language for content autogenerated by Sphinx. Refer to documentation 70 | # for a list of supported languages. 71 | # 72 | # This is also used if you do content translation via gettext catalogs. 73 | # Usually you set 'language' from the command line for these cases. 74 | language = None 75 | 76 | # List of patterns, relative to source directory, that match files and 77 | # directories to ignore when looking for source files. 78 | # This pattern also affects html_static_path and html_extra_path. 79 | exclude_patterns = [u'_build', 'Thumbs.db', '.DS_Store', '**.ipynb_checkpoints'] 80 | 81 | # The name of the Pygments (syntax highlighting) style to use. 82 | pygments_style = None 83 | 84 | # options for nbsphinx 85 | html_scaled_image_link = False 86 | html_sourcelink_suffix = '' 87 | 88 | nbsphinx_execute_arguments = [ 89 | "--InlineBackend.figure_formats={'svg', 'pdf'}", 90 | "--InlineBackend.rc={'figure.dpi': 96}", 91 | ] 92 | nbsphinx_input_prompt = 'In [%s]:' 93 | nbsphinx_output_prompt = 'Out[%s]:' 94 | nbsphinx_timeout = 60 95 | # -- Options for HTML output ------------------------------------------------- 96 | 97 | # The theme to use for HTML and HTML Help pages. See the documentation for 98 | # a list of builtin themes. 99 | # 100 | 101 | import sphinx_rtd_theme 102 | 103 | extensions.append('sphinx_rtd_theme') 104 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 105 | html_theme = 'sphinx_rtd_theme' 106 | 107 | # Theme options are theme-specific and customize the look and feel of a theme 108 | # further. For a list of options available for each theme, see the 109 | # documentation. 110 | # 111 | html_translator_class = 'guzzle_sphinx_theme.HTMLTranslator' 112 | html_show_sourcelink = False 113 | 114 | # html_logo = '_static/brainflow_logo_text.png' 115 | 116 | html_theme_options = { 117 | 'collapse_navigation': False, 118 | 'display_version': True, 119 | 'logo_only': False, 120 | } 121 | 122 | # Add any paths that contain custom static files (such as style sheets) here, 123 | # relative to this directory. They are copied after the builtin static files, 124 | # so a file named 'default.css' will overwrite the builtin 'default.css'. 125 | html_static_path = ['_static'] 126 | # html_context = {'css_files': ['_static/custom.css']} 127 | # Custom sidebar templates, must be a dictionary that maps document names 128 | # to template names. 129 | # 130 | # The default sidebars (for documents that don't match any pattern) are 131 | # defined by theme itself. Builtin themes are using these templates by 132 | # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', 133 | # 'searchbox.html']``. 134 | # 135 | # html_sidebars = {} 136 | 137 | # -- Options for HTMLHelp output --------------------------------------------- 138 | 139 | # Output file base name for HTML help builder. 140 | htmlhelp_basename = 'ScreenRecorderSDKDoc' 141 | 142 | 143 | # -- Options for LaTeX output ------------------------------------------------ 144 | 145 | latex_elements = { 146 | # The paper size ('letterpaper' or 'a4paper'). 147 | # 148 | # 'papersize': 'letterpaper', 149 | 150 | # The font size ('10pt', '11pt' or '12pt'). 151 | # 152 | # 'pointsize': '10pt', 153 | 154 | # Additional stuff for the LaTeX preamble. 155 | # 156 | # 'preamble': '', 157 | 158 | # Latex figure (float) alignment 159 | # 160 | # 'figure_align': 'htbp', 161 | } 162 | 163 | # Grouping the document tree into LaTeX files. List of tuples 164 | # (source start file, target name, title, 165 | # author, documentclass [howto, manual, or own class]). 166 | latex_documents = [ 167 | (master_doc, 'ScreenRecorderSDK.tex', u'ScreenRecorderSDK Documentation', 168 | u'Andrey Parfenov', 'manual'), 169 | ] 170 | 171 | 172 | # -- Options for manual page output ------------------------------------------ 173 | 174 | # One entry per manual page. List of tuples 175 | # (source start file, name, description, authors, manual section). 176 | man_pages = [ 177 | (master_doc, 'ScreenRecorderSDK', u'ScreenRecorderSDK Documentation', 178 | [author], 1) 179 | ] 180 | 181 | 182 | # -- Options for Texinfo output ---------------------------------------------- 183 | 184 | # Grouping the document tree into Texinfo files. List of tuples 185 | # (source start file, target name, title, author, 186 | # dir menu entry, description, category) 187 | texinfo_documents = [ 188 | (master_doc, 'ScreenRecorderSDK', u'ScreenRecorderSDK Documentation', 189 | author, 'ScreenRecorderSDK', 'One line description of project.', 190 | 'Miscellaneous'), 191 | ] 192 | 193 | 194 | # -- Options for Epub output ------------------------------------------------- 195 | 196 | # Bibliographic Dublin Core info. 197 | epub_title = project 198 | 199 | # The unique identifier of the text. This can be a ISBN number 200 | # or the project homepage. 201 | # 202 | # epub_identifier = '' 203 | 204 | # A unique identification for the text. 205 | # 206 | # epub_uid = '' 207 | 208 | # A list of files that should not be packed into the epub file. 209 | epub_exclude_files = ['search.html'] 210 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. BrainFlow documentation master file, created by 2 | sphinx-quickstart on Mon Jul 15 10:23:20 2019. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to Screen Recorder SDK documentation! 7 | ============================================== 8 | 9 | .. toctree:: 10 | Requirements 11 | UserAPI 12 | Examples 13 | License 14 | 15 | 16 | Search 17 | ======== 18 | 19 | * :ref:`search` 20 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx==3.1.2 2 | breathe==4.19.2 3 | sphinx_rtd_theme 4 | pandas 5 | mne 6 | matplotlib 7 | ipykernel 8 | nbsphinx 9 | pandoc 10 | sphinxcontrib-ghcontributors -------------------------------------------------------------------------------- /inc/logger/async_logger.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | // Very fast asynchronous logger (millions of logs per second on an average desktop) 9 | // Uses pre allocated lockfree queue for maximum throughput even under large number of threads. 10 | // Creates a single back thread to pop messages from the queue and log them. 11 | // 12 | // Upon each log write the logger: 13 | // 1. Checks if its log level is enough to log the message 14 | // 2. Push a new copy of the message to a queue (or block the caller until space is available in the queue) 15 | // 3. will throw spdlog_ex upon log exceptions 16 | // Upon destruction, logs all remaining messages in the queue before destructing.. 17 | 18 | #include "common.h" 19 | #include "logger.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | namespace spdlog { 27 | 28 | namespace details { 29 | class async_log_helper; 30 | } 31 | 32 | class async_logger SPDLOG_FINAL : public logger 33 | { 34 | public: 35 | template 36 | async_logger(const std::string &logger_name, const It &begin, const It &end, size_t queue_size, 37 | const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, 38 | const std::function &worker_warmup_cb = nullptr, 39 | const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(), 40 | const std::function &worker_teardown_cb = nullptr); 41 | 42 | async_logger(const std::string &logger_name, sinks_init_list sinks, size_t queue_size, 43 | const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, 44 | const std::function &worker_warmup_cb = nullptr, 45 | const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(), 46 | const std::function &worker_teardown_cb = nullptr); 47 | 48 | async_logger(const std::string &logger_name, sink_ptr single_sink, size_t queue_size, 49 | const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, 50 | const std::function &worker_warmup_cb = nullptr, 51 | const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(), 52 | const std::function &worker_teardown_cb = nullptr); 53 | 54 | // Wait for the queue to be empty, and flush synchronously 55 | // Warning: this can potentially last forever as we wait it to complete 56 | void flush() override; 57 | 58 | // Error handler 59 | void set_error_handler(log_err_handler) override; 60 | log_err_handler error_handler() override; 61 | 62 | protected: 63 | void _sink_it(details::log_msg &msg) override; 64 | void _set_formatter(spdlog::formatter_ptr msg_formatter) override; 65 | void _set_pattern(const std::string &pattern, pattern_time_type pattern_time) override; 66 | 67 | private: 68 | std::unique_ptr _async_log_helper; 69 | }; 70 | } // namespace spdlog 71 | 72 | #include "details/async_logger_impl.h" 73 | -------------------------------------------------------------------------------- /inc/logger/common.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #define SPDLOG_VERSION "0.17.0" 9 | 10 | #include "tweakme.h" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) 22 | #include 23 | #include 24 | #endif 25 | 26 | #include "details/null_mutex.h" 27 | 28 | // visual studio upto 2013 does not support noexcept nor constexpr 29 | #if defined(_MSC_VER) && (_MSC_VER < 1900) 30 | #define SPDLOG_NOEXCEPT throw() 31 | #define SPDLOG_CONSTEXPR 32 | #else 33 | #define SPDLOG_NOEXCEPT noexcept 34 | #define SPDLOG_CONSTEXPR constexpr 35 | #endif 36 | 37 | // final keyword support. On by default. See tweakme.h 38 | #if defined(SPDLOG_NO_FINAL) 39 | #define SPDLOG_FINAL 40 | #else 41 | #define SPDLOG_FINAL final 42 | #endif 43 | 44 | #if defined(__GNUC__) || defined(__clang__) 45 | #define SPDLOG_DEPRECATED __attribute__((deprecated)) 46 | #elif defined(_MSC_VER) 47 | #define SPDLOG_DEPRECATED __declspec(deprecated) 48 | #else 49 | #define SPDLOG_DEPRECATED 50 | #endif 51 | 52 | #include "fmt/fmt.h" 53 | 54 | namespace spdlog { 55 | 56 | class formatter; 57 | 58 | namespace sinks { 59 | class sink; 60 | } 61 | 62 | using log_clock = std::chrono::system_clock; 63 | using sink_ptr = std::shared_ptr; 64 | using sinks_init_list = std::initializer_list; 65 | using formatter_ptr = std::shared_ptr; 66 | #if defined(SPDLOG_NO_ATOMIC_LEVELS) 67 | using level_t = details::null_atomic_int; 68 | #else 69 | using level_t = std::atomic; 70 | #endif 71 | 72 | using log_err_handler = std::function; 73 | 74 | // Log level enum 75 | namespace level { 76 | enum level_enum 77 | { 78 | trace = 0, 79 | debug = 1, 80 | info = 2, 81 | warn = 3, 82 | err = 4, 83 | critical = 5, 84 | off = 6 85 | }; 86 | 87 | #if !defined(SPDLOG_LEVEL_NAMES) 88 | #define SPDLOG_LEVEL_NAMES \ 89 | { \ 90 | "trace", "debug", "info", "warning", "error", "critical", "off" \ 91 | } 92 | #endif 93 | static const char *level_names[] SPDLOG_LEVEL_NAMES; 94 | 95 | static const char *short_level_names[]{"T", "D", "I", "W", "E", "C", "O"}; 96 | 97 | inline const char *to_str(spdlog::level::level_enum l) 98 | { 99 | return level_names[l]; 100 | } 101 | 102 | inline const char *to_short_str(spdlog::level::level_enum l) 103 | { 104 | return short_level_names[l]; 105 | } 106 | inline spdlog::level::level_enum from_str(const std::string &name) 107 | { 108 | static std::unordered_map name_to_level = // map string->level 109 | {{level_names[0], level::trace}, // trace 110 | {level_names[1], level::debug}, // debug 111 | {level_names[2], level::info}, // info 112 | {level_names[3], level::warn}, // warn 113 | {level_names[4], level::err}, // err 114 | {level_names[5], level::critical}, // critical 115 | {level_names[6], level::off}}; // off 116 | 117 | auto lvl_it = name_to_level.find(name); 118 | return lvl_it != name_to_level.end() ? lvl_it->second : level::off; 119 | } 120 | 121 | using level_hasher = std::hash; 122 | } // namespace level 123 | 124 | // 125 | // Async overflow policy - block by default. 126 | // 127 | enum class async_overflow_policy 128 | { 129 | block_retry, // Block / yield / sleep until message can be enqueued 130 | discard_log_msg // Discard the message it enqueue fails 131 | }; 132 | 133 | // 134 | // Pattern time - specific time getting to use for pattern_formatter. 135 | // local time by default 136 | // 137 | enum class pattern_time_type 138 | { 139 | local, // log localtime 140 | utc // log utc 141 | }; 142 | 143 | // 144 | // Log exception 145 | // 146 | class spdlog_ex : public std::exception 147 | { 148 | public: 149 | explicit spdlog_ex(std::string msg) 150 | : _msg(std::move(msg)) 151 | { 152 | } 153 | 154 | spdlog_ex(const std::string &msg, int last_errno) 155 | { 156 | fmt::MemoryWriter writer; 157 | fmt::format_system_error(writer, last_errno, msg); 158 | _msg = writer.str(); 159 | } 160 | 161 | const char *what() const SPDLOG_NOEXCEPT override 162 | { 163 | return _msg.c_str(); 164 | } 165 | 166 | private: 167 | std::string _msg; 168 | }; 169 | 170 | // 171 | // wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) 172 | // 173 | #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) 174 | using filename_t = std::wstring; 175 | #else 176 | using filename_t = std::string; 177 | #endif 178 | 179 | #define SPDLOG_CATCH_AND_HANDLE \ 180 | catch (const std::exception &ex) \ 181 | { \ 182 | _err_handler(ex.what()); \ 183 | } \ 184 | catch (...) \ 185 | { \ 186 | _err_handler("Unknown exeption in logger"); \ 187 | } 188 | } // namespace spdlog 189 | -------------------------------------------------------------------------------- /inc/logger/contrib/README.md: -------------------------------------------------------------------------------- 1 | Please put here your contribs. Popular contribs will be moved to main tree after stablization 2 | -------------------------------------------------------------------------------- /inc/logger/contrib/sinks/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /inc/logger/contrib/sinks/step_file_sink.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../../details/file_helper.h" 4 | #include "../../details/null_mutex.h" 5 | #include "../../fmt/fmt.h" 6 | #include "../../sinks/base_sink.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | // Example for spdlog.h 17 | // 18 | // Create a file logger which creates new files with a specified time step and fixed file size: 19 | // 20 | // std::shared_ptr step_logger_mt(const std::string &logger_name, const filename_t &filename, unsigned seconds = 60, const 21 | // filename_t &tmp_ext = ".tmp", unsigned max_file_size = std::numeric_limits::max(), bool delete_empty_files = true, const 22 | // filename_t &file_header = ""); std::shared_ptr step_logger_st(const std::string &logger_name, const filename_t &filename, 23 | // unsigned seconds = 60, const filename_t &tmp_ext = ".tmp", unsigned max_file_size = std::numeric_limits::max()); 24 | 25 | // Example for spdlog_impl.h 26 | // Create a file logger that creates new files with a specified increment 27 | // inline std::shared_ptr spdlog::step_logger_mt( 28 | // const std::string &logger_name, const filename_t &filename_fmt, unsigned seconds, const filename_t &tmp_ext, unsigned max_file_size, 29 | // bool delete_empty_files, const filename_t &file_header) 30 | // { 31 | // return create(logger_name, filename_fmt, seconds, tmp_ext, max_file_size, delete_empty_files, 32 | // file_header); 33 | // } 34 | 35 | // inline std::shared_ptr spdlog::step_logger_st( 36 | // const std::string &logger_name, const filename_t &filename_fmt, unsigned seconds, const filename_t &tmp_ext, unsigned max_file_size, 37 | // bool delete_empty_files, const filename_t &file_header) 38 | // { 39 | // return create(logger_name, filename_fmt, seconds, tmp_ext, max_file_size, delete_empty_files, 40 | // file_header); 41 | // } 42 | 43 | namespace spdlog { 44 | namespace sinks { 45 | 46 | /* 47 | * Default generator of step log file names. 48 | */ 49 | struct default_step_file_name_calculator 50 | { 51 | // Create filename for the form filename_YYYY-MM-DD_hh-mm-ss.ext 52 | static std::tuple calc_filename(const filename_t &filename, const filename_t &tmp_ext) 53 | { 54 | std::tm tm = spdlog::details::os::localtime(); 55 | filename_t basename, ext; 56 | std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename); 57 | std::conditional::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; 58 | w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 59 | tm.tm_hour, tm.tm_min, tm.tm_sec, tmp_ext); 60 | return std::make_tuple(w.str(), ext); 61 | } 62 | }; 63 | 64 | /* 65 | * Rotating file sink based on size and a specified time step 66 | */ 67 | template 68 | class step_file_sink SPDLOG_FINAL : public base_sink 69 | { 70 | public: 71 | step_file_sink(filename_t base_filename, unsigned step_seconds, filename_t tmp_ext, unsigned max_size, bool delete_empty_files, 72 | filename_t file_header) 73 | : _base_filename(std::move(base_filename)) 74 | , _tmp_ext(std::move(tmp_ext)) 75 | , _step_seconds(step_seconds) 76 | , _max_size(max_size) 77 | , _delete_empty_files(delete_empty_files) 78 | { 79 | if (step_seconds == 0) 80 | { 81 | throw spdlog_ex("step_file_sink: Invalid time step in ctor"); 82 | } 83 | 84 | if (!file_header.empty()) 85 | { 86 | pattern_formatter formatter_for_file_header("%v"); 87 | _file_header.raw << file_header; 88 | formatter_for_file_header.format(_file_header); 89 | } 90 | 91 | if (max_size <= _file_header.formatted.size()) 92 | { 93 | throw spdlog_ex("step_file_sink: Invalid max log size in ctor"); 94 | } 95 | 96 | _tp = _next_tp(); 97 | std::tie(_current_filename, _ext) = FileNameCalc::calc_filename(_base_filename, _tmp_ext); 98 | 99 | if (_tmp_ext == _ext) 100 | { 101 | throw spdlog_ex("step_file_sink: The temporary extension matches the specified in ctor"); 102 | } 103 | 104 | _file_helper.open(_current_filename); 105 | _current_size = _file_helper.size(); // expensive. called only once 106 | 107 | if (!_current_size) 108 | { 109 | _current_size += _file_header.formatted.size(); 110 | if (_current_size) 111 | _file_helper.write(_file_header); 112 | } 113 | } 114 | 115 | ~step_file_sink() 116 | { 117 | try 118 | { 119 | close_current_file(); 120 | } 121 | catch (...) 122 | { 123 | } 124 | } 125 | 126 | protected: 127 | void _sink_it(const details::log_msg &msg) override 128 | { 129 | auto msg_size = msg.formatted.size(); 130 | 131 | if (std::chrono::system_clock::now() >= _tp || _current_size + msg_size > _max_size) 132 | { 133 | filename_t new_filename; 134 | std::tie(new_filename, std::ignore) = FileNameCalc::calc_filename(_base_filename, _tmp_ext); 135 | 136 | bool change_occured = !details::file_helper::file_exists(new_filename); 137 | if (change_occured) 138 | { 139 | close_current_file(); 140 | 141 | _current_filename = std::move(new_filename); 142 | 143 | _file_helper.open(_current_filename); 144 | } 145 | 146 | _tp = _next_tp(); 147 | 148 | if (change_occured) 149 | { 150 | _current_size = _file_header.formatted.size(); 151 | if (_current_size) 152 | _file_helper.write(_file_header); 153 | } 154 | } 155 | 156 | _current_size += msg_size; 157 | _file_helper.write(msg); 158 | } 159 | 160 | void _flush() override 161 | { 162 | _file_helper.flush(); 163 | } 164 | 165 | private: 166 | std::chrono::system_clock::time_point _next_tp() 167 | { 168 | return std::chrono::system_clock::now() + _step_seconds; 169 | } 170 | 171 | void close_current_file() 172 | { 173 | using details::os::filename_to_str; 174 | 175 | // Delete empty files, if required 176 | if (_delete_empty_files && _current_size <= _file_header.formatted.size()) 177 | { 178 | if (details::os::remove(_current_filename) != 0) 179 | { 180 | throw spdlog_ex("step_file_sink: not remove " + filename_to_str(_current_filename), errno); 181 | } 182 | 183 | return; 184 | } 185 | 186 | filename_t target; 187 | std::tie(target, std::ignore) = details::file_helper::split_by_extenstion(_current_filename); 188 | target += _ext; 189 | 190 | if (details::file_helper::file_exists(_current_filename) && details::os::rename(_current_filename, target) != 0) 191 | { 192 | throw spdlog_ex( 193 | "step_file_sink: failed renaming " + filename_to_str(_current_filename) + " to " + filename_to_str(target), errno); 194 | } 195 | } 196 | 197 | const filename_t _base_filename; 198 | const filename_t _tmp_ext; 199 | const std::chrono::seconds _step_seconds; 200 | const unsigned _max_size; 201 | const bool _delete_empty_files; 202 | 203 | std::chrono::system_clock::time_point _tp; 204 | filename_t _current_filename; 205 | filename_t _ext; 206 | unsigned _current_size; 207 | 208 | details::file_helper _file_helper; 209 | details::log_msg _file_header; 210 | }; 211 | 212 | using step_file_sink_mt = step_file_sink; 213 | using step_file_sink_st = step_file_sink; 214 | 215 | } // namespace sinks 216 | } // namespace spdlog 217 | -------------------------------------------------------------------------------- /inc/logger/details/async_log_helper.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | // async log helper : 7 | // Process logs asynchronously using a back thread. 8 | // 9 | // If the internal queue of log messages reaches its max size, 10 | // then the client call will block until there is more room. 11 | // 12 | 13 | #pragma once 14 | 15 | #include "../common.h" 16 | #include "../details/log_msg.h" 17 | #include "../details/mpmc_blocking_q.h" 18 | #include "../details/os.h" 19 | #include "../formatter.h" 20 | #include "../sinks/sink.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | namespace spdlog { 33 | namespace details { 34 | 35 | class async_log_helper 36 | { 37 | // Async msg to move to/from the queue 38 | // Movable only. should never be copied 39 | enum class async_msg_type 40 | { 41 | log, 42 | flush, 43 | terminate 44 | }; 45 | 46 | struct async_msg 47 | { 48 | level::level_enum level; 49 | log_clock::time_point time; 50 | size_t thread_id; 51 | std::string txt; 52 | async_msg_type msg_type; 53 | size_t msg_id; 54 | 55 | async_msg() = default; 56 | ~async_msg() = default; 57 | 58 | explicit async_msg(async_msg_type m_type) 59 | : level(level::info) 60 | , thread_id(0) 61 | , msg_type(m_type) 62 | , msg_id(0) 63 | { 64 | } 65 | 66 | async_msg(async_msg &&other) = default; 67 | async_msg &operator=(async_msg &&other) = default; 68 | 69 | // never copy or assign. should only be moved.. 70 | async_msg(const async_msg &) = delete; 71 | async_msg &operator=(const async_msg &other) = delete; 72 | 73 | // construct from log_msg 74 | explicit async_msg(const details::log_msg &m) 75 | : level(m.level) 76 | , time(m.time) 77 | , thread_id(m.thread_id) 78 | , txt(m.raw.data(), m.raw.size()) 79 | , msg_type(async_msg_type::log) 80 | , msg_id(m.msg_id) 81 | { 82 | } 83 | 84 | // copy into log_msg 85 | void fill_log_msg(log_msg &msg, std::string *logger_name) 86 | { 87 | msg.logger_name = logger_name; 88 | msg.level = level; 89 | msg.time = time; 90 | msg.thread_id = thread_id; 91 | msg.raw.clear(); 92 | msg.raw << txt; 93 | msg.msg_id = msg_id; 94 | } 95 | }; 96 | 97 | public: 98 | using item_type = async_msg; 99 | using q_type = details::mpmc_bounded_queue; 100 | 101 | using clock = std::chrono::steady_clock; 102 | 103 | async_log_helper(std::string logger_name, formatter_ptr formatter, std::vector sinks, size_t queue_size, 104 | const log_err_handler err_handler, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, 105 | std::function worker_warmup_cb = nullptr, 106 | const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(), 107 | std::function worker_teardown_cb = nullptr); 108 | 109 | void log(const details::log_msg &msg); 110 | 111 | // stop logging and join the back thread 112 | ~async_log_helper(); 113 | 114 | async_log_helper(const async_log_helper &) = delete; 115 | async_log_helper &operator=(const async_log_helper &) = delete; 116 | 117 | void set_formatter(formatter_ptr msg_formatter); 118 | 119 | void flush(); 120 | 121 | void set_error_handler(spdlog::log_err_handler err_handler); 122 | 123 | private: 124 | std::string _logger_name; 125 | formatter_ptr _formatter; 126 | std::vector> _sinks; 127 | 128 | // queue of messages to log 129 | q_type _q; 130 | 131 | log_err_handler _err_handler; 132 | 133 | std::chrono::time_point _last_flush; 134 | 135 | // overflow policy 136 | const async_overflow_policy _overflow_policy; 137 | 138 | // worker thread warmup callback - one can set thread priority, affinity, etc 139 | const std::function _worker_warmup_cb; 140 | 141 | // auto periodic sink flush parameter 142 | const std::chrono::milliseconds _flush_interval_ms; 143 | 144 | // worker thread teardown callback 145 | const std::function _worker_teardown_cb; 146 | 147 | std::mutex null_mutex_; 148 | // null_mutex null_mutex_; 149 | std::condition_variable_any not_empty_cv_; 150 | std::condition_variable_any not_full_cv_; 151 | 152 | // worker thread 153 | std::thread _worker_thread; 154 | 155 | void enqueue_msg(async_msg &&new_msg, async_overflow_policy policy); 156 | 157 | // worker thread main loop 158 | void worker_loop(); 159 | 160 | // dequeue next message from the queue and process it. 161 | // return false if termination of the queue is required 162 | bool process_next_msg(); 163 | 164 | void handle_flush_interval(); 165 | 166 | void flush_sinks(); 167 | }; 168 | } // namespace details 169 | } // namespace spdlog 170 | 171 | /////////////////////////////////////////////////////////////////////////////// 172 | // async_sink class implementation 173 | /////////////////////////////////////////////////////////////////////////////// 174 | inline spdlog::details::async_log_helper::async_log_helper(std::string logger_name, formatter_ptr formatter, std::vector sinks, 175 | size_t queue_size, log_err_handler err_handler, const async_overflow_policy overflow_policy, std::function worker_warmup_cb, 176 | const std::chrono::milliseconds &flush_interval_ms, std::function worker_teardown_cb) 177 | : _logger_name(std::move(logger_name)) 178 | , _formatter(std::move(formatter)) 179 | , _sinks(std::move(sinks)) 180 | , _q(queue_size) 181 | , _err_handler(std::move(err_handler)) 182 | , _last_flush(os::now()) 183 | , _overflow_policy(overflow_policy) 184 | , _worker_warmup_cb(std::move(worker_warmup_cb)) 185 | , _flush_interval_ms(flush_interval_ms) 186 | , _worker_teardown_cb(std::move(worker_teardown_cb)) 187 | { 188 | _worker_thread = std::thread(&async_log_helper::worker_loop, this); 189 | } 190 | 191 | // send to the worker thread terminate message, and join it. 192 | inline spdlog::details::async_log_helper::~async_log_helper() 193 | { 194 | try 195 | { 196 | enqueue_msg(async_msg(async_msg_type::terminate), async_overflow_policy::block_retry); 197 | _worker_thread.join(); 198 | } 199 | catch (...) // don't crash in destructor 200 | { 201 | } 202 | } 203 | 204 | // try to push and block until succeeded (if the policy is not to discard when the queue is full) 205 | inline void spdlog::details::async_log_helper::log(const details::log_msg &msg) 206 | { 207 | enqueue_msg(async_msg(msg), _overflow_policy); 208 | } 209 | 210 | inline void spdlog::details::async_log_helper::enqueue_msg(details::async_log_helper::async_msg &&new_msg, async_overflow_policy policy) 211 | { 212 | 213 | // block until succeeded pushing to the queue 214 | if (policy == async_overflow_policy::block_retry) 215 | { 216 | _q.enqueue(std::move(new_msg)); 217 | } 218 | else 219 | { 220 | _q.enqueue_nowait(std::move(new_msg)); 221 | } 222 | } 223 | 224 | // optionally wait for the queue be empty and request flush from the sinks 225 | inline void spdlog::details::async_log_helper::flush() 226 | { 227 | enqueue_msg(async_msg(async_msg_type::flush), _overflow_policy); 228 | } 229 | 230 | inline void spdlog::details::async_log_helper::worker_loop() 231 | { 232 | if (_worker_warmup_cb) 233 | { 234 | _worker_warmup_cb(); 235 | } 236 | auto active = true; 237 | while (active) 238 | { 239 | try 240 | { 241 | active = process_next_msg(); 242 | } 243 | SPDLOG_CATCH_AND_HANDLE 244 | } 245 | if (_worker_teardown_cb) 246 | { 247 | _worker_teardown_cb(); 248 | } 249 | } 250 | 251 | // process next message in the queue 252 | // return true if this thread should still be active (while no terminate msg was received) 253 | inline bool spdlog::details::async_log_helper::process_next_msg() 254 | { 255 | async_msg incoming_async_msg; 256 | bool dequeued = _q.dequeue_for(incoming_async_msg, std::chrono::seconds(2)); 257 | if (!dequeued) 258 | { 259 | handle_flush_interval(); 260 | return true; 261 | } 262 | 263 | switch (incoming_async_msg.msg_type) 264 | { 265 | case async_msg_type::flush: 266 | flush_sinks(); 267 | return true; 268 | 269 | case async_msg_type::terminate: 270 | flush_sinks(); 271 | return false; 272 | 273 | default: 274 | log_msg incoming_log_msg; 275 | incoming_async_msg.fill_log_msg(incoming_log_msg, &_logger_name); 276 | _formatter->format(incoming_log_msg); 277 | for (auto &s : _sinks) 278 | { 279 | if (s->should_log(incoming_log_msg.level)) 280 | { 281 | try 282 | { 283 | s->log(incoming_log_msg); 284 | } 285 | SPDLOG_CATCH_AND_HANDLE 286 | } 287 | } 288 | handle_flush_interval(); 289 | return true; 290 | } 291 | assert(false); 292 | return true; // should not be reached 293 | } 294 | 295 | inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter) 296 | { 297 | _formatter = std::move(msg_formatter); 298 | } 299 | 300 | inline void spdlog::details::async_log_helper::set_error_handler(spdlog::log_err_handler err_handler) 301 | { 302 | _err_handler = std::move(err_handler); 303 | } 304 | 305 | // flush all sinks if _flush_interval_ms has expired. 306 | inline void spdlog::details::async_log_helper::handle_flush_interval() 307 | { 308 | if (_flush_interval_ms == std::chrono::milliseconds::zero()) 309 | { 310 | return; 311 | } 312 | auto delta = details::os::now() - _last_flush; 313 | ; 314 | if (delta >= _flush_interval_ms) 315 | { 316 | flush_sinks(); 317 | } 318 | } 319 | 320 | // flush all sinks if _flush_interval_ms has expired. only called if queue is empty 321 | inline void spdlog::details::async_log_helper::flush_sinks() 322 | { 323 | 324 | for (auto &s : _sinks) 325 | { 326 | try 327 | { 328 | s->flush(); 329 | } 330 | SPDLOG_CATCH_AND_HANDLE 331 | } 332 | _last_flush = os::now(); 333 | } 334 | -------------------------------------------------------------------------------- /inc/logger/details/async_logger_impl.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | // Async Logger implementation 9 | // Use an async_sink (queue per logger) to perform the logging in a worker thread 10 | 11 | #include "../async_logger.h" 12 | #include "../details/async_log_helper.h" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | template 20 | inline spdlog::async_logger::async_logger(const std::string &logger_name, const It &begin, const It &end, size_t queue_size, 21 | const async_overflow_policy overflow_policy, const std::function &worker_warmup_cb, 22 | const std::chrono::milliseconds &flush_interval_ms, const std::function &worker_teardown_cb) 23 | : logger(logger_name, begin, end) 24 | , _async_log_helper(new details::async_log_helper(logger_name, _formatter, _sinks, queue_size, _err_handler, overflow_policy, 25 | worker_warmup_cb, flush_interval_ms, worker_teardown_cb)) 26 | { 27 | } 28 | 29 | inline spdlog::async_logger::async_logger(const std::string &logger_name, sinks_init_list sinks_list, size_t queue_size, 30 | const async_overflow_policy overflow_policy, const std::function &worker_warmup_cb, 31 | const std::chrono::milliseconds &flush_interval_ms, const std::function &worker_teardown_cb) 32 | : async_logger(logger_name, sinks_list.begin(), sinks_list.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, 33 | worker_teardown_cb) 34 | { 35 | } 36 | 37 | inline spdlog::async_logger::async_logger(const std::string &logger_name, sink_ptr single_sink, size_t queue_size, 38 | const async_overflow_policy overflow_policy, const std::function &worker_warmup_cb, 39 | const std::chrono::milliseconds &flush_interval_ms, const std::function &worker_teardown_cb) 40 | : async_logger( 41 | logger_name, {std::move(single_sink)}, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) 42 | { 43 | } 44 | 45 | inline void spdlog::async_logger::flush() 46 | { 47 | _async_log_helper->flush(); 48 | } 49 | 50 | // Error handler 51 | inline void spdlog::async_logger::set_error_handler(spdlog::log_err_handler err_handler) 52 | { 53 | _err_handler = err_handler; 54 | _async_log_helper->set_error_handler(err_handler); 55 | } 56 | inline spdlog::log_err_handler spdlog::async_logger::error_handler() 57 | { 58 | return _err_handler; 59 | } 60 | 61 | inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter) 62 | { 63 | _formatter = msg_formatter; 64 | _async_log_helper->set_formatter(_formatter); 65 | } 66 | 67 | inline void spdlog::async_logger::_set_pattern(const std::string &pattern, pattern_time_type pattern_time) 68 | { 69 | _formatter = std::make_shared(pattern, pattern_time); 70 | _async_log_helper->set_formatter(_formatter); 71 | } 72 | 73 | inline void spdlog::async_logger::_sink_it(details::log_msg &msg) 74 | { 75 | try 76 | { 77 | #if defined(SPDLOG_ENABLE_MESSAGE_COUNTER) 78 | _incr_msg_counter(msg); 79 | #endif 80 | _async_log_helper->log(msg); 81 | if (_should_flush_on(msg)) 82 | { 83 | _async_log_helper->flush(); // do async flush 84 | } 85 | } 86 | catch (const std::exception &ex) 87 | { 88 | _err_handler(ex.what()); 89 | } 90 | catch (...) 91 | { 92 | _err_handler("Unknown exception in logger " + _name); 93 | throw; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /inc/logger/details/file_helper.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | // Helper class for file sink 9 | // When failing to open a file, retry several times(5) with small delay between the tries(10 ms) 10 | // Throw spdlog_ex exception on errors 11 | 12 | #include "../details/log_msg.h" 13 | #include "../details/os.h" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | namespace spdlog { 23 | namespace details { 24 | 25 | class file_helper 26 | { 27 | 28 | public: 29 | const int open_tries = 5; 30 | const int open_interval = 10; 31 | 32 | explicit file_helper() = default; 33 | 34 | file_helper(const file_helper &) = delete; 35 | file_helper &operator=(const file_helper &) = delete; 36 | 37 | ~file_helper() 38 | { 39 | close(); 40 | } 41 | 42 | void open(const filename_t &fname, bool truncate = false) 43 | { 44 | close(); 45 | auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab"); 46 | _filename = fname; 47 | for (int tries = 0; tries < open_tries; ++tries) 48 | { 49 | if (!os::fopen_s(&_fd, fname, mode)) 50 | { 51 | return; 52 | } 53 | 54 | details::os::sleep_for_millis(open_interval); 55 | } 56 | 57 | throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno); 58 | } 59 | 60 | void reopen(bool truncate) 61 | { 62 | if (_filename.empty()) 63 | { 64 | throw spdlog_ex("Failed re opening file - was not opened before"); 65 | } 66 | open(_filename, truncate); 67 | } 68 | 69 | void flush() 70 | { 71 | std::fflush(_fd); 72 | } 73 | 74 | void close() 75 | { 76 | if (_fd != nullptr) 77 | { 78 | std::fclose(_fd); 79 | _fd = nullptr; 80 | } 81 | } 82 | 83 | void write(const log_msg &msg) 84 | { 85 | size_t msg_size = msg.formatted.size(); 86 | auto data = msg.formatted.data(); 87 | if (std::fwrite(data, 1, msg_size, _fd) != msg_size) 88 | { 89 | throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno); 90 | } 91 | } 92 | 93 | size_t size() const 94 | { 95 | if (_fd == nullptr) 96 | { 97 | throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename)); 98 | } 99 | return os::filesize(_fd); 100 | } 101 | 102 | const filename_t &filename() const 103 | { 104 | return _filename; 105 | } 106 | 107 | static bool file_exists(const filename_t &fname) 108 | { 109 | return os::file_exists(fname); 110 | } 111 | 112 | // 113 | // return file path and its extension: 114 | // 115 | // "mylog.txt" => ("mylog", ".txt") 116 | // "mylog" => ("mylog", "") 117 | // "mylog." => ("mylog.", "") 118 | // "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt") 119 | // 120 | // the starting dot in filenames is ignored (hidden files): 121 | // 122 | // ".mylog" => (".mylog". "") 123 | // "my_folder/.mylog" => ("my_folder/.mylog", "") 124 | // "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt") 125 | static std::tuple split_by_extenstion(const spdlog::filename_t &fname) 126 | { 127 | auto ext_index = fname.rfind('.'); 128 | 129 | // no valid extension found - return whole path and empty string as extension 130 | if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1) 131 | { 132 | return std::make_tuple(fname, spdlog::filename_t()); 133 | } 134 | 135 | // treat casese like "/etc/rc.d/somelogfile or "/abc/.hiddenfile" 136 | auto folder_index = fname.rfind(details::os::folder_sep); 137 | if (folder_index != fname.npos && folder_index >= ext_index - 1) 138 | { 139 | return std::make_tuple(fname, spdlog::filename_t()); 140 | } 141 | 142 | // finally - return a valid base and extension tuple 143 | return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index)); 144 | } 145 | 146 | private: 147 | FILE *_fd{nullptr}; 148 | filename_t _filename; 149 | }; 150 | } // namespace details 151 | } // namespace spdlog 152 | -------------------------------------------------------------------------------- /inc/logger/details/log_msg.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #include "../common.h" 9 | #include "../details/os.h" 10 | 11 | #include 12 | #include 13 | 14 | namespace spdlog { 15 | namespace details { 16 | struct log_msg 17 | { 18 | log_msg() = default; 19 | log_msg(const std::string *loggers_name, level::level_enum lvl) 20 | : logger_name(loggers_name) 21 | , level(lvl) 22 | { 23 | #ifndef SPDLOG_NO_DATETIME 24 | time = os::now(); 25 | #endif 26 | 27 | #ifndef SPDLOG_NO_THREAD_ID 28 | thread_id = os::thread_id(); 29 | #endif 30 | } 31 | 32 | log_msg(const log_msg &other) = delete; 33 | log_msg &operator=(log_msg &&other) = delete; 34 | log_msg(log_msg &&other) = delete; 35 | 36 | const std::string *logger_name{nullptr}; 37 | level::level_enum level; 38 | log_clock::time_point time; 39 | size_t thread_id; 40 | fmt::MemoryWriter raw; 41 | fmt::MemoryWriter formatted; 42 | size_t msg_id{0}; 43 | // wrap this range with color codes 44 | size_t color_range_start{0}; 45 | size_t color_range_end{0}; 46 | }; 47 | } // namespace details 48 | } // namespace spdlog 49 | -------------------------------------------------------------------------------- /inc/logger/details/logger_impl.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #include "../logger.h" 9 | 10 | #include 11 | #include 12 | 13 | // create logger with given name, sinks and the default pattern formatter 14 | // all other ctors will call this one 15 | template 16 | inline spdlog::logger::logger(std::string logger_name, const It &begin, const It &end) 17 | : _name(std::move(logger_name)) 18 | , _sinks(begin, end) 19 | , _formatter(std::make_shared("%+")) 20 | , _level(level::info) 21 | , _flush_level(level::off) 22 | , _last_err_time(0) 23 | , _msg_counter(1) // message counter will start from 1. 0-message id will be reserved for controll messages 24 | { 25 | _err_handler = [this](const std::string &msg) { this->_default_err_handler(msg); }; 26 | } 27 | 28 | // ctor with sinks as init list 29 | inline spdlog::logger::logger(const std::string &logger_name, sinks_init_list sinks_list) 30 | : logger(logger_name, sinks_list.begin(), sinks_list.end()) 31 | { 32 | } 33 | 34 | // ctor with single sink 35 | inline spdlog::logger::logger(const std::string &logger_name, spdlog::sink_ptr single_sink) 36 | : logger(logger_name, {std::move(single_sink)}) 37 | { 38 | } 39 | 40 | inline spdlog::logger::~logger() = default; 41 | 42 | inline void spdlog::logger::set_formatter(spdlog::formatter_ptr msg_formatter) 43 | { 44 | _set_formatter(std::move(msg_formatter)); 45 | } 46 | 47 | inline void spdlog::logger::set_pattern(const std::string &pattern, pattern_time_type pattern_time) 48 | { 49 | _set_pattern(pattern, pattern_time); 50 | } 51 | 52 | template 53 | inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Args &... args) 54 | { 55 | if (!should_log(lvl)) 56 | { 57 | return; 58 | } 59 | 60 | try 61 | { 62 | details::log_msg log_msg(&_name, lvl); 63 | 64 | #if defined(SPDLOG_FMT_PRINTF) 65 | fmt::printf(log_msg.raw, fmt, args...); 66 | #else 67 | log_msg.raw.write(fmt, args...); 68 | #endif 69 | _sink_it(log_msg); 70 | } 71 | SPDLOG_CATCH_AND_HANDLE 72 | } 73 | 74 | template 75 | inline void spdlog::logger::log(level::level_enum lvl, const char *msg) 76 | { 77 | if (!should_log(lvl)) 78 | { 79 | return; 80 | } 81 | try 82 | { 83 | details::log_msg log_msg(&_name, lvl); 84 | log_msg.raw << msg; 85 | _sink_it(log_msg); 86 | } 87 | SPDLOG_CATCH_AND_HANDLE 88 | } 89 | 90 | template 91 | inline void spdlog::logger::log(level::level_enum lvl, const T &msg) 92 | { 93 | if (!should_log(lvl)) 94 | { 95 | return; 96 | } 97 | try 98 | { 99 | details::log_msg log_msg(&_name, lvl); 100 | log_msg.raw << msg; 101 | _sink_it(log_msg); 102 | } 103 | SPDLOG_CATCH_AND_HANDLE 104 | } 105 | 106 | template 107 | inline void spdlog::logger::trace(const char *fmt, const Arg1 &arg1, const Args &... args) 108 | { 109 | log(level::trace, fmt, arg1, args...); 110 | } 111 | 112 | template 113 | inline void spdlog::logger::debug(const char *fmt, const Arg1 &arg1, const Args &... args) 114 | { 115 | log(level::debug, fmt, arg1, args...); 116 | } 117 | 118 | template 119 | inline void spdlog::logger::info(const char *fmt, const Arg1 &arg1, const Args &... args) 120 | { 121 | log(level::info, fmt, arg1, args...); 122 | } 123 | 124 | template 125 | inline void spdlog::logger::warn(const char *fmt, const Arg1 &arg1, const Args &... args) 126 | { 127 | log(level::warn, fmt, arg1, args...); 128 | } 129 | 130 | template 131 | inline void spdlog::logger::error(const char *fmt, const Arg1 &arg1, const Args &... args) 132 | { 133 | log(level::err, fmt, arg1, args...); 134 | } 135 | 136 | template 137 | inline void spdlog::logger::critical(const char *fmt, const Arg1 &arg1, const Args &... args) 138 | { 139 | log(level::critical, fmt, arg1, args...); 140 | } 141 | 142 | template 143 | inline void spdlog::logger::trace(const T &msg) 144 | { 145 | log(level::trace, msg); 146 | } 147 | 148 | template 149 | inline void spdlog::logger::debug(const T &msg) 150 | { 151 | log(level::debug, msg); 152 | } 153 | 154 | template 155 | inline void spdlog::logger::info(const T &msg) 156 | { 157 | log(level::info, msg); 158 | } 159 | 160 | template 161 | inline void spdlog::logger::warn(const T &msg) 162 | { 163 | log(level::warn, msg); 164 | } 165 | 166 | template 167 | inline void spdlog::logger::error(const T &msg) 168 | { 169 | log(level::err, msg); 170 | } 171 | 172 | template 173 | inline void spdlog::logger::critical(const T &msg) 174 | { 175 | log(level::critical, msg); 176 | } 177 | 178 | #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT 179 | #include 180 | #include 181 | 182 | template 183 | inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *msg) 184 | { 185 | std::wstring_convert> conv; 186 | 187 | log(lvl, conv.to_bytes(msg)); 188 | } 189 | 190 | template 191 | inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const Args &... args) 192 | { 193 | fmt::WMemoryWriter wWriter; 194 | 195 | wWriter.write(fmt, args...); 196 | log(lvl, wWriter.c_str()); 197 | } 198 | 199 | template 200 | inline void spdlog::logger::trace(const wchar_t *fmt, const Args &... args) 201 | { 202 | log(level::trace, fmt, args...); 203 | } 204 | 205 | template 206 | inline void spdlog::logger::debug(const wchar_t *fmt, const Args &... args) 207 | { 208 | log(level::debug, fmt, args...); 209 | } 210 | 211 | template 212 | inline void spdlog::logger::info(const wchar_t *fmt, const Args &... args) 213 | { 214 | log(level::info, fmt, args...); 215 | } 216 | 217 | template 218 | inline void spdlog::logger::warn(const wchar_t *fmt, const Args &... args) 219 | { 220 | log(level::warn, fmt, args...); 221 | } 222 | 223 | template 224 | inline void spdlog::logger::error(const wchar_t *fmt, const Args &... args) 225 | { 226 | log(level::err, fmt, args...); 227 | } 228 | 229 | template 230 | inline void spdlog::logger::critical(const wchar_t *fmt, const Args &... args) 231 | { 232 | log(level::critical, fmt, args...); 233 | } 234 | 235 | #endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT 236 | 237 | // 238 | // name and level 239 | // 240 | inline const std::string &spdlog::logger::name() const 241 | { 242 | return _name; 243 | } 244 | 245 | inline void spdlog::logger::set_level(spdlog::level::level_enum log_level) 246 | { 247 | _level.store(log_level); 248 | } 249 | 250 | inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler) 251 | { 252 | _err_handler = std::move(err_handler); 253 | } 254 | 255 | inline spdlog::log_err_handler spdlog::logger::error_handler() 256 | { 257 | return _err_handler; 258 | } 259 | 260 | inline void spdlog::logger::flush_on(level::level_enum log_level) 261 | { 262 | _flush_level.store(log_level); 263 | } 264 | 265 | inline spdlog::level::level_enum spdlog::logger::level() const 266 | { 267 | return static_cast(_level.load(std::memory_order_relaxed)); 268 | } 269 | 270 | inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) const 271 | { 272 | return msg_level >= _level.load(std::memory_order_relaxed); 273 | } 274 | 275 | // 276 | // protected virtual called at end of each user log call (if enabled) by the line_logger 277 | // 278 | inline void spdlog::logger::_sink_it(details::log_msg &msg) 279 | { 280 | #if defined(SPDLOG_ENABLE_MESSAGE_COUNTER) 281 | _incr_msg_counter(msg); 282 | #endif 283 | _formatter->format(msg); 284 | for (auto &sink : _sinks) 285 | { 286 | if (sink->should_log(msg.level)) 287 | { 288 | sink->log(msg); 289 | } 290 | } 291 | 292 | if (_should_flush_on(msg)) 293 | { 294 | flush(); 295 | } 296 | } 297 | 298 | inline void spdlog::logger::_set_pattern(const std::string &pattern, pattern_time_type pattern_time) 299 | { 300 | _formatter = std::make_shared(pattern, pattern_time); 301 | } 302 | 303 | inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter) 304 | { 305 | _formatter = std::move(msg_formatter); 306 | } 307 | 308 | inline void spdlog::logger::flush() 309 | { 310 | try 311 | { 312 | for (auto &sink : _sinks) 313 | { 314 | sink->flush(); 315 | } 316 | } 317 | SPDLOG_CATCH_AND_HANDLE 318 | } 319 | 320 | inline void spdlog::logger::_default_err_handler(const std::string &msg) 321 | { 322 | auto now = time(nullptr); 323 | if (now - _last_err_time < 60) 324 | { 325 | return; 326 | } 327 | _last_err_time = now; 328 | auto tm_time = details::os::localtime(now); 329 | char date_buf[100]; 330 | std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time); 331 | fmt::print(stderr, "[*** LOG ERROR ***] [{}] [{}] {}\n", date_buf, name(), msg); 332 | } 333 | 334 | inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg) 335 | { 336 | const auto flush_level = _flush_level.load(std::memory_order_relaxed); 337 | return (msg.level >= flush_level) && (msg.level != level::off); 338 | } 339 | 340 | inline void spdlog::logger::_incr_msg_counter(details::log_msg &msg) 341 | { 342 | msg.msg_id = _msg_counter.fetch_add(1, std::memory_order_relaxed); 343 | } 344 | 345 | inline const std::vector &spdlog::logger::sinks() const 346 | { 347 | return _sinks; 348 | } 349 | -------------------------------------------------------------------------------- /inc/logger/details/mpmc_blocking_q.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright(c) 2018 Gabi Melman. 5 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 6 | // 7 | 8 | // async log helper : 9 | // multi producer-multi consumer blocking queue 10 | // enqueue(..) - will block until room found to put the new message 11 | // enqueue_nowait(..) - will return immediatly with false if no room left in the queue 12 | // dequeue_for(..) - will block until the queue is not empty or timeout passed 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | namespace spdlog { 19 | namespace details { 20 | 21 | template 22 | class mpmc_bounded_queue 23 | { 24 | public: 25 | using item_type = T; 26 | explicit mpmc_bounded_queue(size_t max_items) 27 | : max_items_(max_items) 28 | { 29 | } 30 | 31 | // try to enqueue and block if no room left 32 | void enqueue(T &&item) 33 | { 34 | { 35 | std::unique_lock lock(queue_mutex_); 36 | pop_cv_.wait(lock, [this] { return this->q_.size() < this->max_items_; }); 37 | q_.push(std::move(item)); 38 | } 39 | push_cv_.notify_one(); 40 | } 41 | 42 | // try to enqueue and return immdeialty false if no room left 43 | bool enqueue_nowait(T &&item) 44 | { 45 | { 46 | std::unique_lock lock(queue_mutex_); 47 | if (q_.size() == this->max_items_) 48 | { 49 | return false; 50 | } 51 | q_.push(std::forward(item)); 52 | } 53 | push_cv_.notify_one(); 54 | return true; 55 | } 56 | 57 | // try to dequeue item. if no item found. wait upto timeout and try again 58 | // Return true, if succeeded dequeue item, false otherwise 59 | bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) 60 | { 61 | { 62 | std::unique_lock lock(queue_mutex_); 63 | if (!push_cv_.wait_for(lock, wait_duration, [this] { return this->q_.size() > 0; })) 64 | { 65 | return false; 66 | } 67 | 68 | popped_item = std::move(q_.front()); 69 | q_.pop(); 70 | } 71 | pop_cv_.notify_one(); 72 | return true; 73 | } 74 | 75 | private: 76 | size_t max_items_; 77 | std::mutex queue_mutex_; 78 | std::condition_variable push_cv_; 79 | std::condition_variable pop_cv_; 80 | 81 | std::queue q_; 82 | }; 83 | } // namespace details 84 | } // namespace spdlog 85 | -------------------------------------------------------------------------------- /inc/logger/details/null_mutex.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #include 9 | // null, no cost dummy "mutex" and dummy "atomic" int 10 | 11 | namespace spdlog { 12 | namespace details { 13 | struct null_mutex 14 | { 15 | void lock() {} 16 | void unlock() {} 17 | bool try_lock() 18 | { 19 | return true; 20 | } 21 | }; 22 | 23 | struct null_atomic_int 24 | { 25 | int value; 26 | null_atomic_int() = default; 27 | 28 | explicit null_atomic_int(int val) 29 | : value(val) 30 | { 31 | } 32 | 33 | int load(std::memory_order) const 34 | { 35 | return value; 36 | } 37 | 38 | void store(int val) 39 | { 40 | value = val; 41 | } 42 | }; 43 | 44 | } // namespace details 45 | } // namespace spdlog 46 | -------------------------------------------------------------------------------- /inc/logger/details/registry.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | // Loggers registy of unique name->logger pointer 9 | // An attempt to create a logger with an already existing name will be ignored 10 | // If user requests a non existing logger, nullptr will be returned 11 | // This class is thread safe 12 | 13 | #include "../async_logger.h" 14 | #include "../common.h" 15 | #include "../details/null_mutex.h" 16 | #include "../logger.h" 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | namespace spdlog { 26 | namespace details { 27 | template 28 | class registry_t 29 | { 30 | public: 31 | registry_t(const registry_t &) = delete; 32 | registry_t &operator=(const registry_t &) = delete; 33 | 34 | void register_logger(std::shared_ptr logger) 35 | { 36 | std::lock_guard lock(_mutex); 37 | auto logger_name = logger->name(); 38 | throw_if_exists(logger_name); 39 | _loggers[logger_name] = logger; 40 | } 41 | 42 | std::shared_ptr get(const std::string &logger_name) 43 | { 44 | std::lock_guard lock(_mutex); 45 | auto found = _loggers.find(logger_name); 46 | return found == _loggers.end() ? nullptr : found->second; 47 | } 48 | 49 | template 50 | std::shared_ptr create(const std::string &logger_name, const It &sinks_begin, const It &sinks_end) 51 | { 52 | std::lock_guard lock(_mutex); 53 | throw_if_exists(logger_name); 54 | std::shared_ptr new_logger; 55 | if (_async_mode) 56 | { 57 | new_logger = std::make_shared(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, 58 | _worker_warmup_cb, _flush_interval_ms, _worker_teardown_cb); 59 | } 60 | else 61 | { 62 | new_logger = std::make_shared(logger_name, sinks_begin, sinks_end); 63 | } 64 | 65 | if (_formatter) 66 | { 67 | new_logger->set_formatter(_formatter); 68 | } 69 | 70 | if (_err_handler) 71 | { 72 | new_logger->set_error_handler(_err_handler); 73 | } 74 | 75 | new_logger->set_level(_level); 76 | new_logger->flush_on(_flush_level); 77 | 78 | // Add to registry 79 | _loggers[logger_name] = new_logger; 80 | return new_logger; 81 | } 82 | 83 | template 84 | std::shared_ptr create_async(const std::string &logger_name, size_t queue_size, 85 | const async_overflow_policy overflow_policy, const std::function &worker_warmup_cb, 86 | const std::chrono::milliseconds &flush_interval_ms, const std::function &worker_teardown_cb, const It &sinks_begin, 87 | const It &sinks_end) 88 | { 89 | std::lock_guard lock(_mutex); 90 | throw_if_exists(logger_name); 91 | auto new_logger = std::make_shared( 92 | logger_name, sinks_begin, sinks_end, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb); 93 | 94 | if (_formatter) 95 | { 96 | new_logger->set_formatter(_formatter); 97 | } 98 | 99 | if (_err_handler) 100 | { 101 | new_logger->set_error_handler(_err_handler); 102 | } 103 | 104 | new_logger->set_level(_level); 105 | new_logger->flush_on(_flush_level); 106 | 107 | // Add to registry 108 | _loggers[logger_name] = new_logger; 109 | return new_logger; 110 | } 111 | 112 | void apply_all(std::function)> fun) 113 | { 114 | std::lock_guard lock(_mutex); 115 | for (auto &l : _loggers) 116 | { 117 | fun(l.second); 118 | } 119 | } 120 | 121 | void drop(const std::string &logger_name) 122 | { 123 | std::lock_guard lock(_mutex); 124 | _loggers.erase(logger_name); 125 | } 126 | 127 | void drop_all() 128 | { 129 | std::lock_guard lock(_mutex); 130 | _loggers.clear(); 131 | } 132 | 133 | std::shared_ptr create(const std::string &logger_name, sinks_init_list sinks) 134 | { 135 | return create(logger_name, sinks.begin(), sinks.end()); 136 | } 137 | 138 | std::shared_ptr create(const std::string &logger_name, sink_ptr sink) 139 | { 140 | return create(logger_name, {sink}); 141 | } 142 | 143 | std::shared_ptr create_async(const std::string &logger_name, size_t queue_size, 144 | const async_overflow_policy overflow_policy, const std::function &worker_warmup_cb, 145 | const std::chrono::milliseconds &flush_interval_ms, const std::function &worker_teardown_cb, sinks_init_list sinks) 146 | { 147 | return create_async( 148 | logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks.begin(), sinks.end()); 149 | } 150 | 151 | std::shared_ptr create_async(const std::string &logger_name, size_t queue_size, 152 | const async_overflow_policy overflow_policy, const std::function &worker_warmup_cb, 153 | const std::chrono::milliseconds &flush_interval_ms, const std::function &worker_teardown_cb, sink_ptr sink) 154 | { 155 | return create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, {sink}); 156 | } 157 | 158 | void formatter(formatter_ptr f) 159 | { 160 | std::lock_guard lock(_mutex); 161 | _formatter = f; 162 | for (auto &l : _loggers) 163 | { 164 | l.second->set_formatter(_formatter); 165 | } 166 | } 167 | 168 | void set_pattern(const std::string &pattern) 169 | { 170 | std::lock_guard lock(_mutex); 171 | _formatter = std::make_shared(pattern); 172 | for (auto &l : _loggers) 173 | { 174 | l.second->set_formatter(_formatter); 175 | } 176 | } 177 | 178 | void set_level(level::level_enum log_level) 179 | { 180 | std::lock_guard lock(_mutex); 181 | for (auto &l : _loggers) 182 | { 183 | l.second->set_level(log_level); 184 | } 185 | _level = log_level; 186 | } 187 | 188 | void flush_on(level::level_enum log_level) 189 | { 190 | std::lock_guard lock(_mutex); 191 | for (auto &l : _loggers) 192 | { 193 | l.second->flush_on(log_level); 194 | } 195 | _flush_level = log_level; 196 | } 197 | 198 | void set_error_handler(log_err_handler handler) 199 | { 200 | for (auto &l : _loggers) 201 | { 202 | l.second->set_error_handler(handler); 203 | } 204 | _err_handler = handler; 205 | } 206 | 207 | void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function &worker_warmup_cb, 208 | const std::chrono::milliseconds &flush_interval_ms, const std::function &worker_teardown_cb) 209 | { 210 | std::lock_guard lock(_mutex); 211 | _async_mode = true; 212 | _async_q_size = q_size; 213 | _overflow_policy = overflow_policy; 214 | _worker_warmup_cb = worker_warmup_cb; 215 | _flush_interval_ms = flush_interval_ms; 216 | _worker_teardown_cb = worker_teardown_cb; 217 | } 218 | 219 | void set_sync_mode() 220 | { 221 | std::lock_guard lock(_mutex); 222 | _async_mode = false; 223 | } 224 | 225 | static registry_t &instance() 226 | { 227 | static registry_t s_instance; 228 | return s_instance; 229 | } 230 | 231 | private: 232 | registry_t() = default; 233 | 234 | void throw_if_exists(const std::string &logger_name) 235 | { 236 | if (_loggers.find(logger_name) != _loggers.end()) 237 | { 238 | throw spdlog_ex("logger with name '" + logger_name + "' already exists"); 239 | } 240 | } 241 | 242 | Mutex _mutex; 243 | std::unordered_map> _loggers; 244 | formatter_ptr _formatter; 245 | level::level_enum _level = level::info; 246 | level::level_enum _flush_level = level::off; 247 | log_err_handler _err_handler; 248 | bool _async_mode = false; 249 | size_t _async_q_size = 0; 250 | async_overflow_policy _overflow_policy = async_overflow_policy::block_retry; 251 | std::function _worker_warmup_cb; 252 | std::chrono::milliseconds _flush_interval_ms{std::chrono::milliseconds::zero()}; 253 | std::function _worker_teardown_cb; 254 | }; 255 | 256 | #ifdef SPDLOG_NO_REGISTRY_MUTEX 257 | using registry = registry_t; 258 | #else 259 | using registry = registry_t; 260 | #endif 261 | 262 | } // namespace details 263 | } // namespace spdlog 264 | -------------------------------------------------------------------------------- /inc/logger/fmt/bundled/LICENSE.rst: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 - 2016, Victor Zverovich 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /inc/logger/fmt/bundled/ostream.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Formatting library for C++ - std::ostream support 3 | 4 | Copyright (c) 2012 - 2016, Victor Zverovich 5 | All rights reserved. 6 | 7 | For the license information refer to format.h. 8 | */ 9 | 10 | #include "ostream.h" 11 | 12 | namespace fmt { 13 | 14 | namespace internal { 15 | FMT_FUNC void write(std::ostream &os, Writer &w) { 16 | const char *data = w.data(); 17 | typedef internal::MakeUnsigned::Type UnsignedStreamSize; 18 | UnsignedStreamSize size = w.size(); 19 | UnsignedStreamSize max_size = 20 | internal::to_unsigned((std::numeric_limits::max)()); 21 | do { 22 | UnsignedStreamSize n = size <= max_size ? size : max_size; 23 | os.write(data, static_cast(n)); 24 | data += n; 25 | size -= n; 26 | } while (size != 0); 27 | } 28 | } 29 | 30 | FMT_FUNC void print(std::ostream &os, CStringRef format_str, ArgList args) { 31 | MemoryWriter w; 32 | w.write(format_str, args); 33 | internal::write(os, w); 34 | } 35 | } // namespace fmt 36 | -------------------------------------------------------------------------------- /inc/logger/fmt/bundled/ostream.h: -------------------------------------------------------------------------------- 1 | /* 2 | Formatting library for C++ - std::ostream support 3 | 4 | Copyright (c) 2012 - 2016, Victor Zverovich 5 | All rights reserved. 6 | 7 | For the license information refer to format.h. 8 | */ 9 | 10 | #ifndef FMT_OSTREAM_H_ 11 | #define FMT_OSTREAM_H_ 12 | 13 | #include "format.h" 14 | #include 15 | 16 | namespace fmt { 17 | 18 | namespace internal { 19 | 20 | template 21 | class FormatBuf : public std::basic_streambuf 22 | { 23 | private: 24 | typedef typename std::basic_streambuf::int_type int_type; 25 | typedef typename std::basic_streambuf::traits_type traits_type; 26 | 27 | Buffer &buffer_; 28 | 29 | public: 30 | FormatBuf(Buffer &buffer) 31 | : buffer_(buffer) 32 | { 33 | } 34 | 35 | protected: 36 | // The put-area is actually always empty. This makes the implementation 37 | // simpler and has the advantage that the streambuf and the buffer are always 38 | // in sync and sputc never writes into uninitialized memory. The obvious 39 | // disadvantage is that each call to sputc always results in a (virtual) call 40 | // to overflow. There is no disadvantage here for sputn since this always 41 | // results in a call to xsputn. 42 | 43 | int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE 44 | { 45 | if (!traits_type::eq_int_type(ch, traits_type::eof())) 46 | buffer_.push_back(static_cast(ch)); 47 | return ch; 48 | } 49 | 50 | std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE 51 | { 52 | buffer_.append(s, s + count); 53 | return count; 54 | } 55 | }; 56 | 57 | Yes &convert(std::ostream &); 58 | 59 | struct DummyStream : std::ostream 60 | { 61 | DummyStream(); // Suppress a bogus warning in MSVC. 62 | 63 | // Hide all operator<< overloads from std::ostream. 64 | template 65 | typename EnableIf::type operator<<(const T &); 66 | }; 67 | 68 | No &operator<<(std::ostream &, int); 69 | 70 | template 71 | struct ConvertToIntImpl 72 | { 73 | // Convert to int only if T doesn't have an overloaded operator<<. 74 | enum 75 | { 76 | value = sizeof(convert(get() << get())) == sizeof(No) 77 | }; 78 | }; 79 | 80 | // Write the content of w to os. 81 | FMT_API void write(std::ostream &os, Writer &w); 82 | } // namespace internal 83 | 84 | // Formats a value. 85 | template 86 | void format_arg(BasicFormatter &f, const Char *&format_str, const T &value) 87 | { 88 | internal::MemoryBuffer buffer; 89 | 90 | internal::FormatBuf format_buf(buffer); 91 | std::basic_ostream output(&format_buf); 92 | output.exceptions(std::ios_base::failbit | std::ios_base::badbit); 93 | output << value; 94 | 95 | BasicStringRef str(&buffer[0], buffer.size()); 96 | typedef internal::MakeArg> MakeArg; 97 | format_str = f.format(format_str, MakeArg(str)); 98 | } 99 | 100 | /** 101 | \rst 102 | Prints formatted data to the stream *os*. 103 | 104 | **Example**:: 105 | 106 | print(cerr, "Don't {}!", "panic"); 107 | \endrst 108 | */ 109 | FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args); 110 | FMT_VARIADIC(void, print, std::ostream &, CStringRef) 111 | } // namespace fmt 112 | 113 | #ifdef FMT_HEADER_ONLY 114 | #include "ostream.cc" 115 | #endif 116 | 117 | #endif // FMT_OSTREAM_H_ 118 | -------------------------------------------------------------------------------- /inc/logger/fmt/bundled/posix.cc: -------------------------------------------------------------------------------- 1 | /* 2 | A C++ interface to POSIX functions. 3 | 4 | Copyright (c) 2012 - 2016, Victor Zverovich 5 | All rights reserved. 6 | 7 | For the license information refer to format.h. 8 | */ 9 | 10 | // Disable bogus MSVC warnings. 11 | #ifndef _CRT_SECURE_NO_WARNINGS 12 | # define _CRT_SECURE_NO_WARNINGS 13 | #endif 14 | 15 | #include "posix.h" 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #ifndef _WIN32 22 | # include 23 | #else 24 | # ifndef WIN32_LEAN_AND_MEAN 25 | # define WIN32_LEAN_AND_MEAN 26 | # endif 27 | # include 28 | # include 29 | 30 | # define O_CREAT _O_CREAT 31 | # define O_TRUNC _O_TRUNC 32 | 33 | # ifndef S_IRUSR 34 | # define S_IRUSR _S_IREAD 35 | # endif 36 | 37 | # ifndef S_IWUSR 38 | # define S_IWUSR _S_IWRITE 39 | # endif 40 | 41 | # ifdef __MINGW32__ 42 | # define _SH_DENYNO 0x40 43 | # endif 44 | 45 | #endif // _WIN32 46 | 47 | #ifdef fileno 48 | # undef fileno 49 | #endif 50 | 51 | namespace { 52 | #ifdef _WIN32 53 | // Return type of read and write functions. 54 | typedef int RWResult; 55 | 56 | // On Windows the count argument to read and write is unsigned, so convert 57 | // it from size_t preventing integer overflow. 58 | inline unsigned convert_rwcount(std::size_t count) { 59 | return count <= UINT_MAX ? static_cast(count) : UINT_MAX; 60 | } 61 | #else 62 | // Return type of read and write functions. 63 | typedef ssize_t RWResult; 64 | 65 | inline std::size_t convert_rwcount(std::size_t count) { return count; } 66 | #endif 67 | } 68 | 69 | fmt::BufferedFile::~BufferedFile() FMT_NOEXCEPT { 70 | if (file_ && FMT_SYSTEM(fclose(file_)) != 0) 71 | fmt::report_system_error(errno, "cannot close file"); 72 | } 73 | 74 | fmt::BufferedFile::BufferedFile( 75 | fmt::CStringRef filename, fmt::CStringRef mode) { 76 | FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 0); 77 | if (!file_) 78 | FMT_THROW(SystemError(errno, "cannot open file {}", filename)); 79 | } 80 | 81 | void fmt::BufferedFile::close() { 82 | if (!file_) 83 | return; 84 | int result = FMT_SYSTEM(fclose(file_)); 85 | file_ = FMT_NULL; 86 | if (result != 0) 87 | FMT_THROW(SystemError(errno, "cannot close file")); 88 | } 89 | 90 | // A macro used to prevent expansion of fileno on broken versions of MinGW. 91 | #define FMT_ARGS 92 | 93 | int fmt::BufferedFile::fileno() const { 94 | int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_)); 95 | if (fd == -1) 96 | FMT_THROW(SystemError(errno, "cannot get file descriptor")); 97 | return fd; 98 | } 99 | 100 | fmt::File::File(fmt::CStringRef path, int oflag) { 101 | int mode = S_IRUSR | S_IWUSR; 102 | #if defined(_WIN32) && !defined(__MINGW32__) 103 | fd_ = -1; 104 | FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); 105 | #else 106 | FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode))); 107 | #endif 108 | if (fd_ == -1) 109 | FMT_THROW(SystemError(errno, "cannot open file {}", path)); 110 | } 111 | 112 | fmt::File::~File() FMT_NOEXCEPT { 113 | // Don't retry close in case of EINTR! 114 | // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html 115 | if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) 116 | fmt::report_system_error(errno, "cannot close file"); 117 | } 118 | 119 | void fmt::File::close() { 120 | if (fd_ == -1) 121 | return; 122 | // Don't retry close in case of EINTR! 123 | // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html 124 | int result = FMT_POSIX_CALL(close(fd_)); 125 | fd_ = -1; 126 | if (result != 0) 127 | FMT_THROW(SystemError(errno, "cannot close file")); 128 | } 129 | 130 | fmt::LongLong fmt::File::size() const { 131 | #ifdef _WIN32 132 | // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT 133 | // is less than 0x0500 as is the case with some default MinGW builds. 134 | // Both functions support large file sizes. 135 | DWORD size_upper = 0; 136 | HANDLE handle = reinterpret_cast(_get_osfhandle(fd_)); 137 | DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper)); 138 | if (size_lower == INVALID_FILE_SIZE) { 139 | DWORD error = GetLastError(); 140 | if (error != NO_ERROR) 141 | FMT_THROW(WindowsError(GetLastError(), "cannot get file size")); 142 | } 143 | fmt::ULongLong long_size = size_upper; 144 | return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower; 145 | #else 146 | typedef struct stat Stat; 147 | Stat file_stat = Stat(); 148 | if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) 149 | FMT_THROW(SystemError(errno, "cannot get file attributes")); 150 | FMT_STATIC_ASSERT(sizeof(fmt::LongLong) >= sizeof(file_stat.st_size), 151 | "return type of File::size is not large enough"); 152 | return file_stat.st_size; 153 | #endif 154 | } 155 | 156 | std::size_t fmt::File::read(void *buffer, std::size_t count) { 157 | RWResult result = 0; 158 | FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); 159 | if (result < 0) 160 | FMT_THROW(SystemError(errno, "cannot read from file")); 161 | return internal::to_unsigned(result); 162 | } 163 | 164 | std::size_t fmt::File::write(const void *buffer, std::size_t count) { 165 | RWResult result = 0; 166 | FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); 167 | if (result < 0) 168 | FMT_THROW(SystemError(errno, "cannot write to file")); 169 | return internal::to_unsigned(result); 170 | } 171 | 172 | fmt::File fmt::File::dup(int fd) { 173 | // Don't retry as dup doesn't return EINTR. 174 | // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html 175 | int new_fd = FMT_POSIX_CALL(dup(fd)); 176 | if (new_fd == -1) 177 | FMT_THROW(SystemError(errno, "cannot duplicate file descriptor {}", fd)); 178 | return File(new_fd); 179 | } 180 | 181 | void fmt::File::dup2(int fd) { 182 | int result = 0; 183 | FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); 184 | if (result == -1) { 185 | FMT_THROW(SystemError(errno, 186 | "cannot duplicate file descriptor {} to {}", fd_, fd)); 187 | } 188 | } 189 | 190 | void fmt::File::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT { 191 | int result = 0; 192 | FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); 193 | if (result == -1) 194 | ec = ErrorCode(errno); 195 | } 196 | 197 | void fmt::File::pipe(File &read_end, File &write_end) { 198 | // Close the descriptors first to make sure that assignments don't throw 199 | // and there are no leaks. 200 | read_end.close(); 201 | write_end.close(); 202 | int fds[2] = {}; 203 | #ifdef _WIN32 204 | // Make the default pipe capacity same as on Linux 2.6.11+. 205 | enum { DEFAULT_CAPACITY = 65536 }; 206 | int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY)); 207 | #else 208 | // Don't retry as the pipe function doesn't return EINTR. 209 | // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html 210 | int result = FMT_POSIX_CALL(pipe(fds)); 211 | #endif 212 | if (result != 0) 213 | FMT_THROW(SystemError(errno, "cannot create pipe")); 214 | // The following assignments don't throw because read_fd and write_fd 215 | // are closed. 216 | read_end = File(fds[0]); 217 | write_end = File(fds[1]); 218 | } 219 | 220 | fmt::BufferedFile fmt::File::fdopen(const char *mode) { 221 | // Don't retry as fdopen doesn't return EINTR. 222 | FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode)); 223 | if (!f) 224 | FMT_THROW(SystemError(errno, "cannot associate stream with file descriptor")); 225 | BufferedFile file(f); 226 | fd_ = -1; 227 | return file; 228 | } 229 | 230 | long fmt::getpagesize() { 231 | #ifdef _WIN32 232 | SYSTEM_INFO si; 233 | GetSystemInfo(&si); 234 | return si.dwPageSize; 235 | #else 236 | long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); 237 | if (size < 0) 238 | FMT_THROW(SystemError(errno, "cannot get memory page size")); 239 | return size; 240 | #endif 241 | } 242 | -------------------------------------------------------------------------------- /inc/logger/fmt/bundled/printf.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Formatting library for C++ 3 | 4 | Copyright (c) 2012 - 2016, Victor Zverovich 5 | All rights reserved. 6 | 7 | For the license information refer to format.h. 8 | */ 9 | 10 | #include "format.h" 11 | #include "printf.h" 12 | 13 | namespace fmt { 14 | 15 | template 16 | void printf(BasicWriter &w, BasicCStringRef format, ArgList args); 17 | 18 | FMT_FUNC int fprintf(std::FILE *f, CStringRef format, ArgList args) { 19 | MemoryWriter w; 20 | printf(w, format, args); 21 | std::size_t size = w.size(); 22 | return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast(size); 23 | } 24 | 25 | #ifndef FMT_HEADER_ONLY 26 | 27 | template void PrintfFormatter::format(CStringRef format); 28 | template void PrintfFormatter::format(WCStringRef format); 29 | 30 | #endif // FMT_HEADER_ONLY 31 | 32 | } // namespace fmt 33 | -------------------------------------------------------------------------------- /inc/logger/fmt/bundled/time.h: -------------------------------------------------------------------------------- 1 | /* 2 | Formatting library for C++ - time formatting 3 | 4 | Copyright (c) 2012 - 2016, Victor Zverovich 5 | All rights reserved. 6 | 7 | For the license information refer to format.h. 8 | */ 9 | 10 | #ifndef FMT_TIME_H_ 11 | #define FMT_TIME_H_ 12 | 13 | #include "format.h" 14 | #include 15 | 16 | #ifdef _MSC_VER 17 | #pragma warning(push) 18 | #pragma warning(disable : 4702) // unreachable code 19 | #pragma warning(disable : 4996) // "deprecated" functions 20 | #endif 21 | 22 | namespace fmt { 23 | template 24 | void format_arg(BasicFormatter &f, const char *&format_str, const std::tm &tm) 25 | { 26 | if (*format_str == ':') 27 | ++format_str; 28 | const char *end = format_str; 29 | while (*end && *end != '}') 30 | ++end; 31 | if (*end != '}') 32 | FMT_THROW(FormatError("missing '}' in format string")); 33 | internal::MemoryBuffer format; 34 | format.append(format_str, end + 1); 35 | format[format.size() - 1] = '\0'; 36 | Buffer &buffer = f.writer().buffer(); 37 | std::size_t start = buffer.size(); 38 | for (;;) 39 | { 40 | std::size_t size = buffer.capacity() - start; 41 | std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm); 42 | if (count != 0) 43 | { 44 | buffer.resize(start + count); 45 | break; 46 | } 47 | if (size >= format.size() * 256) 48 | { 49 | // If the buffer is 256 times larger than the format string, assume 50 | // that `strftime` gives an empty result. There doesn't seem to be a 51 | // better way to distinguish the two cases: 52 | // https://github.com/fmtlib/fmt/issues/367 53 | break; 54 | } 55 | const std::size_t MIN_GROWTH = 10; 56 | buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); 57 | } 58 | format_str = end + 1; 59 | } 60 | 61 | namespace internal { 62 | inline Null<> localtime_r(...) 63 | { 64 | return Null<>(); 65 | } 66 | inline Null<> localtime_s(...) 67 | { 68 | return Null<>(); 69 | } 70 | inline Null<> gmtime_r(...) 71 | { 72 | return Null<>(); 73 | } 74 | inline Null<> gmtime_s(...) 75 | { 76 | return Null<>(); 77 | } 78 | } // namespace internal 79 | 80 | // Thread-safe replacement for std::localtime 81 | inline std::tm localtime(std::time_t time) 82 | { 83 | struct LocalTime 84 | { 85 | std::time_t time_; 86 | std::tm tm_; 87 | 88 | LocalTime(std::time_t t) 89 | : time_(t) 90 | { 91 | } 92 | 93 | bool run() 94 | { 95 | using namespace fmt::internal; 96 | return handle(localtime_r(&time_, &tm_)); 97 | } 98 | 99 | bool handle(std::tm *tm) 100 | { 101 | return tm != FMT_NULL; 102 | } 103 | 104 | bool handle(internal::Null<>) 105 | { 106 | using namespace fmt::internal; 107 | return fallback(localtime_s(&tm_, &time_)); 108 | } 109 | 110 | bool fallback(int res) 111 | { 112 | return res == 0; 113 | } 114 | 115 | bool fallback(internal::Null<>) 116 | { 117 | using namespace fmt::internal; 118 | std::tm *tm = std::localtime(&time_); 119 | if (tm) 120 | tm_ = *tm; 121 | return tm != FMT_NULL; 122 | } 123 | }; 124 | LocalTime lt(time); 125 | if (lt.run()) 126 | return lt.tm_; 127 | // Too big time values may be unsupported. 128 | FMT_THROW(fmt::FormatError("time_t value out of range")); 129 | return std::tm(); 130 | } 131 | 132 | // Thread-safe replacement for std::gmtime 133 | inline std::tm gmtime(std::time_t time) 134 | { 135 | struct GMTime 136 | { 137 | std::time_t time_; 138 | std::tm tm_; 139 | 140 | GMTime(std::time_t t) 141 | : time_(t) 142 | { 143 | } 144 | 145 | bool run() 146 | { 147 | using namespace fmt::internal; 148 | return handle(gmtime_r(&time_, &tm_)); 149 | } 150 | 151 | bool handle(std::tm *tm) 152 | { 153 | return tm != FMT_NULL; 154 | } 155 | 156 | bool handle(internal::Null<>) 157 | { 158 | using namespace fmt::internal; 159 | return fallback(gmtime_s(&tm_, &time_)); 160 | } 161 | 162 | bool fallback(int res) 163 | { 164 | return res == 0; 165 | } 166 | 167 | bool fallback(internal::Null<>) 168 | { 169 | std::tm *tm = std::gmtime(&time_); 170 | if (tm != FMT_NULL) 171 | tm_ = *tm; 172 | return tm != FMT_NULL; 173 | } 174 | }; 175 | GMTime gt(time); 176 | if (gt.run()) 177 | return gt.tm_; 178 | // Too big time values may be unsupported. 179 | FMT_THROW(fmt::FormatError("time_t value out of range")); 180 | return std::tm(); 181 | } 182 | } // namespace fmt 183 | 184 | #ifdef _MSC_VER 185 | #pragma warning(pop) 186 | #endif 187 | 188 | #endif // FMT_TIME_H_ 189 | -------------------------------------------------------------------------------- /inc/logger/fmt/fmt.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2016 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | // 9 | // Include a bundled header-only copy of fmtlib or an external one. 10 | // By default spdlog include its own copy. 11 | // 12 | 13 | #if !defined(SPDLOG_FMT_EXTERNAL) 14 | 15 | #ifndef FMT_HEADER_ONLY 16 | #define FMT_HEADER_ONLY 17 | #endif 18 | #ifndef FMT_USE_WINDOWS_H 19 | #define FMT_USE_WINDOWS_H 0 20 | #endif 21 | #include "bundled/format.h" 22 | #if defined(SPDLOG_FMT_PRINTF) 23 | #include "bundled/printf.h" 24 | #endif 25 | 26 | #else // external fmtlib 27 | 28 | #include 29 | #if defined(SPDLOG_FMT_PRINTF) 30 | #include 31 | #endif 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /inc/logger/fmt/ostr.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2016 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | // 8 | // include bundled or external copy of fmtlib's ostream support 9 | // 10 | #if !defined(SPDLOG_FMT_EXTERNAL) 11 | #ifndef FMT_HEADER_ONLY 12 | #define FMT_HEADER_ONLY 13 | #endif 14 | #include "bundled/ostream.h" 15 | #include "fmt.h" 16 | #else 17 | #include 18 | #endif 19 | -------------------------------------------------------------------------------- /inc/logger/formatter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #include "details/log_msg.h" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace spdlog { 15 | namespace details { 16 | class flag_formatter; 17 | } 18 | 19 | class formatter 20 | { 21 | public: 22 | virtual ~formatter() = default; 23 | virtual void format(details::log_msg &msg) = 0; 24 | }; 25 | 26 | class pattern_formatter SPDLOG_FINAL : public formatter 27 | { 28 | public: 29 | explicit pattern_formatter(const std::string &pattern, pattern_time_type pattern_time = pattern_time_type::local, 30 | std::string eol = spdlog::details::os::default_eol); 31 | pattern_formatter(const pattern_formatter &) = delete; 32 | pattern_formatter &operator=(const pattern_formatter &) = delete; 33 | void format(details::log_msg &msg) override; 34 | 35 | private: 36 | const std::string _eol; 37 | const std::string _pattern; 38 | const pattern_time_type _pattern_time; 39 | std::vector> _formatters; 40 | std::tm get_time(details::log_msg &msg); 41 | void handle_flag(char flag); 42 | void compile_pattern(const std::string &pattern); 43 | }; 44 | } // namespace spdlog 45 | 46 | #include "details/pattern_formatter_impl.h" 47 | -------------------------------------------------------------------------------- /inc/logger/logger.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | // Thread safe logger (except for set_pattern(..), set_formatter(..) and set_error_handler()) 9 | // Has name, log level, vector of std::shared sink pointers and formatter 10 | // Upon each log write the logger: 11 | // 1. Checks if its log level is enough to log the message 12 | // 2. Format the message using the formatter function 13 | // 3. Pass the formatted message to its sinks to performa the actual logging 14 | 15 | #include "common.h" 16 | #include "sinks/base_sink.h" 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | namespace spdlog { 23 | 24 | class logger 25 | { 26 | public: 27 | logger(const std::string &name, sink_ptr single_sink); 28 | logger(const std::string &name, sinks_init_list sinks); 29 | 30 | template 31 | logger(std::string name, const It &begin, const It &end); 32 | 33 | virtual ~logger(); 34 | 35 | logger(const logger &) = delete; 36 | logger &operator=(const logger &) = delete; 37 | 38 | template 39 | void log(level::level_enum lvl, const char *fmt, const Args &... args); 40 | 41 | template 42 | void log(level::level_enum lvl, const char *msg); 43 | 44 | template 45 | void trace(const char *fmt, const Arg1 &, const Args &... args); 46 | 47 | template 48 | void debug(const char *fmt, const Arg1 &, const Args &... args); 49 | 50 | template 51 | void info(const char *fmt, const Arg1 &, const Args &... args); 52 | 53 | template 54 | void warn(const char *fmt, const Arg1 &, const Args &... args); 55 | 56 | template 57 | void error(const char *fmt, const Arg1 &, const Args &... args); 58 | 59 | template 60 | void critical(const char *fmt, const Arg1 &, const Args &... args); 61 | 62 | #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT 63 | template 64 | void log(level::level_enum lvl, const wchar_t *msg); 65 | 66 | template 67 | void log(level::level_enum lvl, const wchar_t *fmt, const Args &... args); 68 | 69 | template 70 | void trace(const wchar_t *fmt, const Args &... args); 71 | 72 | template 73 | void debug(const wchar_t *fmt, const Args &... args); 74 | 75 | template 76 | void info(const wchar_t *fmt, const Args &... args); 77 | 78 | template 79 | void warn(const wchar_t *fmt, const Args &... args); 80 | 81 | template 82 | void error(const wchar_t *fmt, const Args &... args); 83 | 84 | template 85 | void critical(const wchar_t *fmt, const Args &... args); 86 | #endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT 87 | 88 | template 89 | void log(level::level_enum lvl, const T &); 90 | 91 | template 92 | void trace(const T &msg); 93 | 94 | template 95 | void debug(const T &msg); 96 | 97 | template 98 | void info(const T &msg); 99 | 100 | template 101 | void warn(const T &msg); 102 | 103 | template 104 | void error(const T &msg); 105 | 106 | template 107 | void critical(const T &msg); 108 | 109 | bool should_log(level::level_enum msg_level) const; 110 | void set_level(level::level_enum log_level); 111 | level::level_enum level() const; 112 | const std::string &name() const; 113 | void set_pattern(const std::string &pattern, pattern_time_type pattern_time = pattern_time_type::local); 114 | void set_formatter(formatter_ptr msg_formatter); 115 | 116 | // automatically call flush() if message level >= log_level 117 | void flush_on(level::level_enum log_level); 118 | 119 | virtual void flush(); 120 | 121 | const std::vector &sinks() const; 122 | 123 | // error handler 124 | virtual void set_error_handler(log_err_handler err_handler); 125 | virtual log_err_handler error_handler(); 126 | 127 | protected: 128 | virtual void _sink_it(details::log_msg &msg); 129 | virtual void _set_pattern(const std::string &pattern, pattern_time_type pattern_time); 130 | virtual void _set_formatter(formatter_ptr msg_formatter); 131 | 132 | // default error handler: print the error to stderr with the max rate of 1 message/minute 133 | virtual void _default_err_handler(const std::string &msg); 134 | 135 | // return true if the given message level should trigger a flush 136 | bool _should_flush_on(const details::log_msg &msg); 137 | 138 | // increment the message count (only if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)) 139 | void _incr_msg_counter(details::log_msg &msg); 140 | 141 | const std::string _name; 142 | std::vector _sinks; 143 | formatter_ptr _formatter; 144 | spdlog::level_t _level; 145 | spdlog::level_t _flush_level; 146 | log_err_handler _err_handler; 147 | std::atomic _last_err_time; 148 | std::atomic _msg_counter; 149 | }; 150 | } // namespace spdlog 151 | 152 | #include "details/logger_impl.h" 153 | -------------------------------------------------------------------------------- /inc/logger/sinks/android_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #if defined(__ANDROID__) 9 | 10 | #include "../details/os.h" 11 | #include "sink.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #if !defined(SPDLOG_ANDROID_RETRIES) 20 | #define SPDLOG_ANDROID_RETRIES 2 21 | #endif 22 | 23 | namespace spdlog { 24 | namespace sinks { 25 | 26 | /* 27 | * Android sink (logging using __android_log_write) 28 | * __android_log_write is thread-safe. No lock is needed. 29 | */ 30 | class android_sink : public sink 31 | { 32 | public: 33 | explicit android_sink(const std::string &tag = "spdlog", bool use_raw_msg = false) 34 | : _tag(tag) 35 | , _use_raw_msg(use_raw_msg) 36 | { 37 | } 38 | 39 | void log(const details::log_msg &msg) override 40 | { 41 | const android_LogPriority priority = convert_to_android(msg.level); 42 | const char *msg_output = (_use_raw_msg ? msg.raw.c_str() : msg.formatted.c_str()); 43 | 44 | // See system/core/liblog/logger_write.c for explanation of return value 45 | int ret = __android_log_write(priority, _tag.c_str(), msg_output); 46 | int retry_count = 0; 47 | while ((ret == -11 /*EAGAIN*/) && (retry_count < SPDLOG_ANDROID_RETRIES)) 48 | { 49 | details::os::sleep_for_millis(5); 50 | ret = __android_log_write(priority, _tag.c_str(), msg_output); 51 | retry_count++; 52 | } 53 | 54 | if (ret < 0) 55 | { 56 | throw spdlog_ex("__android_log_write() failed", ret); 57 | } 58 | } 59 | 60 | void flush() override {} 61 | 62 | private: 63 | static android_LogPriority convert_to_android(spdlog::level::level_enum level) 64 | { 65 | switch (level) 66 | { 67 | case spdlog::level::trace: 68 | return ANDROID_LOG_VERBOSE; 69 | case spdlog::level::debug: 70 | return ANDROID_LOG_DEBUG; 71 | case spdlog::level::info: 72 | return ANDROID_LOG_INFO; 73 | case spdlog::level::warn: 74 | return ANDROID_LOG_WARN; 75 | case spdlog::level::err: 76 | return ANDROID_LOG_ERROR; 77 | case spdlog::level::critical: 78 | return ANDROID_LOG_FATAL; 79 | default: 80 | return ANDROID_LOG_DEFAULT; 81 | } 82 | } 83 | 84 | std::string _tag; 85 | bool _use_raw_msg; 86 | }; 87 | 88 | } // namespace sinks 89 | } // namespace spdlog 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /inc/logger/sinks/ansicolor_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2017 spdlog authors. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #include "../common.h" 9 | #include "../details/os.h" 10 | #include "base_sink.h" 11 | 12 | #include 13 | #include 14 | 15 | namespace spdlog { 16 | namespace sinks { 17 | 18 | /** 19 | * This sink prefixes the output with an ANSI escape sequence color code depending on the severity 20 | * of the message. 21 | * If no color terminal detected, omit the escape codes. 22 | */ 23 | template 24 | class ansicolor_sink : public base_sink 25 | { 26 | public: 27 | explicit ansicolor_sink(FILE *file) 28 | : target_file_(file) 29 | { 30 | should_do_colors_ = details::os::in_terminal(file) && details::os::is_color_terminal(); 31 | colors_[level::trace] = white; 32 | colors_[level::debug] = cyan; 33 | colors_[level::info] = green; 34 | colors_[level::warn] = yellow + bold; 35 | colors_[level::err] = red + bold; 36 | colors_[level::critical] = bold + on_red; 37 | colors_[level::off] = reset; 38 | } 39 | 40 | ~ansicolor_sink() override 41 | { 42 | _flush(); 43 | } 44 | 45 | void set_color(level::level_enum color_level, const std::string &color) 46 | { 47 | std::lock_guard lock(base_sink::_mutex); 48 | colors_[color_level] = color; 49 | } 50 | 51 | /// Formatting codes 52 | const std::string reset = "\033[m"; 53 | const std::string bold = "\033[1m"; 54 | const std::string dark = "\033[2m"; 55 | const std::string underline = "\033[4m"; 56 | const std::string blink = "\033[5m"; 57 | const std::string reverse = "\033[7m"; 58 | const std::string concealed = "\033[8m"; 59 | const std::string clear_line = "\033[K"; 60 | 61 | // Foreground colors 62 | const std::string black = "\033[30m"; 63 | const std::string red = "\033[31m"; 64 | const std::string green = "\033[32m"; 65 | const std::string yellow = "\033[33m"; 66 | const std::string blue = "\033[34m"; 67 | const std::string magenta = "\033[35m"; 68 | const std::string cyan = "\033[36m"; 69 | const std::string white = "\033[37m"; 70 | 71 | /// Background colors 72 | const std::string on_black = "\033[40m"; 73 | const std::string on_red = "\033[41m"; 74 | const std::string on_green = "\033[42m"; 75 | const std::string on_yellow = "\033[43m"; 76 | const std::string on_blue = "\033[44m"; 77 | const std::string on_magenta = "\033[45m"; 78 | const std::string on_cyan = "\033[46m"; 79 | const std::string on_white = "\033[47m"; 80 | 81 | protected: 82 | void _sink_it(const details::log_msg &msg) override 83 | { 84 | // Wrap the originally formatted message in color codes. 85 | // If color is not supported in the terminal, log as is instead. 86 | if (should_do_colors_ && msg.color_range_end > msg.color_range_start) 87 | { 88 | // before color range 89 | _print_range(msg, 0, msg.color_range_start); 90 | // in color range 91 | _print_ccode(colors_[msg.level]); 92 | _print_range(msg, msg.color_range_start, msg.color_range_end); 93 | _print_ccode(reset); 94 | // after color range 95 | _print_range(msg, msg.color_range_end, msg.formatted.size()); 96 | } 97 | else 98 | { 99 | _print_range(msg, 0, msg.formatted.size()); 100 | } 101 | _flush(); 102 | } 103 | 104 | void _flush() override 105 | { 106 | fflush(target_file_); 107 | } 108 | 109 | private: 110 | void _print_ccode(const std::string &color_code) 111 | { 112 | fwrite(color_code.data(), sizeof(char), color_code.size(), target_file_); 113 | } 114 | void _print_range(const details::log_msg &msg, size_t start, size_t end) 115 | { 116 | fwrite(msg.formatted.data() + start, sizeof(char), end - start, target_file_); 117 | } 118 | FILE *target_file_; 119 | bool should_do_colors_; 120 | std::unordered_map colors_; 121 | }; 122 | 123 | template 124 | class ansicolor_stdout_sink : public ansicolor_sink 125 | { 126 | public: 127 | ansicolor_stdout_sink() 128 | : ansicolor_sink(stdout) 129 | { 130 | } 131 | }; 132 | 133 | using ansicolor_stdout_sink_mt = ansicolor_stdout_sink; 134 | using ansicolor_stdout_sink_st = ansicolor_stdout_sink; 135 | 136 | template 137 | class ansicolor_stderr_sink : public ansicolor_sink 138 | { 139 | public: 140 | ansicolor_stderr_sink() 141 | : ansicolor_sink(stderr) 142 | { 143 | } 144 | }; 145 | 146 | using ansicolor_stderr_sink_mt = ansicolor_stderr_sink; 147 | using ansicolor_stderr_sink_st = ansicolor_stderr_sink; 148 | 149 | } // namespace sinks 150 | } // namespace spdlog 151 | -------------------------------------------------------------------------------- /inc/logger/sinks/base_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | // 8 | // base sink templated over a mutex (either dummy or real) 9 | // concrete implementation should only override the _sink_it method. 10 | // all locking is taken care of here so no locking needed by the implementers.. 11 | // 12 | 13 | #include "../common.h" 14 | #include "../details/log_msg.h" 15 | #include "../formatter.h" 16 | #include "sink.h" 17 | 18 | #include 19 | 20 | namespace spdlog { 21 | namespace sinks { 22 | template 23 | class base_sink : public sink 24 | { 25 | public: 26 | base_sink() = default; 27 | 28 | base_sink(const base_sink &) = delete; 29 | base_sink &operator=(const base_sink &) = delete; 30 | 31 | void log(const details::log_msg &msg) SPDLOG_FINAL override 32 | { 33 | std::lock_guard lock(_mutex); 34 | _sink_it(msg); 35 | } 36 | 37 | void flush() SPDLOG_FINAL override 38 | { 39 | std::lock_guard lock(_mutex); 40 | _flush(); 41 | } 42 | 43 | protected: 44 | virtual void _sink_it(const details::log_msg &msg) = 0; 45 | virtual void _flush() = 0; 46 | Mutex _mutex; 47 | }; 48 | } // namespace sinks 49 | } // namespace spdlog 50 | -------------------------------------------------------------------------------- /inc/logger/sinks/dist_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015 David Schury, Gabi Melman 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #include "../details/log_msg.h" 9 | #include "../details/null_mutex.h" 10 | #include "base_sink.h" 11 | #include "sink.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | // Distribution sink (mux). Stores a vector of sinks which get called when log is called 19 | 20 | namespace spdlog { 21 | namespace sinks { 22 | template 23 | class dist_sink : public base_sink 24 | { 25 | public: 26 | explicit dist_sink() 27 | : _sinks() 28 | { 29 | } 30 | dist_sink(const dist_sink &) = delete; 31 | dist_sink &operator=(const dist_sink &) = delete; 32 | 33 | protected: 34 | std::vector> _sinks; 35 | 36 | void _sink_it(const details::log_msg &msg) override 37 | { 38 | for (auto &sink : _sinks) 39 | { 40 | if (sink->should_log(msg.level)) 41 | { 42 | sink->log(msg); 43 | } 44 | } 45 | } 46 | 47 | void _flush() override 48 | { 49 | for (auto &sink : _sinks) 50 | sink->flush(); 51 | } 52 | 53 | public: 54 | void add_sink(std::shared_ptr sink) 55 | { 56 | std::lock_guard lock(base_sink::_mutex); 57 | _sinks.push_back(sink); 58 | } 59 | 60 | void remove_sink(std::shared_ptr sink) 61 | { 62 | std::lock_guard lock(base_sink::_mutex); 63 | _sinks.erase(std::remove(_sinks.begin(), _sinks.end(), sink), _sinks.end()); 64 | } 65 | }; 66 | 67 | using dist_sink_mt = dist_sink; 68 | using dist_sink_st = dist_sink; 69 | 70 | } // namespace sinks 71 | } // namespace spdlog 72 | -------------------------------------------------------------------------------- /inc/logger/sinks/file_sinks.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #include "../details/file_helper.h" 9 | #include "../details/null_mutex.h" 10 | #include "../fmt/fmt.h" 11 | #include "base_sink.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | namespace spdlog { 22 | namespace sinks { 23 | /* 24 | * Trivial file sink with single file as target 25 | */ 26 | template 27 | class simple_file_sink SPDLOG_FINAL : public base_sink 28 | { 29 | public: 30 | explicit simple_file_sink(const filename_t &filename, bool truncate = false) 31 | : _force_flush(false) 32 | { 33 | _file_helper.open(filename, truncate); 34 | } 35 | 36 | void set_force_flush(bool force_flush) 37 | { 38 | _force_flush = force_flush; 39 | } 40 | 41 | protected: 42 | void _sink_it(const details::log_msg &msg) override 43 | { 44 | _file_helper.write(msg); 45 | if (_force_flush) 46 | { 47 | _file_helper.flush(); 48 | } 49 | } 50 | 51 | void _flush() override 52 | { 53 | _file_helper.flush(); 54 | } 55 | 56 | private: 57 | details::file_helper _file_helper; 58 | bool _force_flush; 59 | }; 60 | 61 | using simple_file_sink_mt = simple_file_sink; 62 | using simple_file_sink_st = simple_file_sink; 63 | 64 | /* 65 | * Rotating file sink based on size 66 | */ 67 | template 68 | class rotating_file_sink SPDLOG_FINAL : public base_sink 69 | { 70 | public: 71 | rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files) 72 | : _base_filename(std::move(base_filename)) 73 | , _max_size(max_size) 74 | , _max_files(max_files) 75 | { 76 | _file_helper.open(calc_filename(_base_filename, 0)); 77 | _current_size = _file_helper.size(); // expensive. called only once 78 | } 79 | 80 | // calc filename according to index and file extension if exists. 81 | // e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt". 82 | static filename_t calc_filename(const filename_t &filename, std::size_t index) 83 | { 84 | typename std::conditional::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; 85 | if (index != 0u) 86 | { 87 | filename_t basename, ext; 88 | std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename); 89 | w.write(SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext); 90 | } 91 | else 92 | { 93 | w.write(SPDLOG_FILENAME_T("{}"), filename); 94 | } 95 | return w.str(); 96 | } 97 | 98 | protected: 99 | void _sink_it(const details::log_msg &msg) override 100 | { 101 | _current_size += msg.formatted.size(); 102 | if (_current_size > _max_size) 103 | { 104 | _rotate(); 105 | _current_size = msg.formatted.size(); 106 | } 107 | _file_helper.write(msg); 108 | } 109 | 110 | void _flush() override 111 | { 112 | _file_helper.flush(); 113 | } 114 | 115 | private: 116 | // Rotate files: 117 | // log.txt -> log.1.txt 118 | // log.1.txt -> log.2.txt 119 | // log.2.txt -> log.3.txt 120 | // log.3.txt -> delete 121 | void _rotate() 122 | { 123 | using details::os::filename_to_str; 124 | _file_helper.close(); 125 | for (auto i = _max_files; i > 0; --i) 126 | { 127 | filename_t src = calc_filename(_base_filename, i - 1); 128 | filename_t target = calc_filename(_base_filename, i); 129 | 130 | if (details::file_helper::file_exists(target)) 131 | { 132 | if (details::os::remove(target) != 0) 133 | { 134 | throw spdlog_ex("rotating_file_sink: failed removing " + filename_to_str(target), errno); 135 | } 136 | } 137 | if (details::file_helper::file_exists(src) && details::os::rename(src, target) != 0) 138 | { 139 | throw spdlog_ex("rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno); 140 | } 141 | } 142 | _file_helper.reopen(true); 143 | } 144 | 145 | filename_t _base_filename; 146 | std::size_t _max_size; 147 | std::size_t _max_files; 148 | std::size_t _current_size; 149 | details::file_helper _file_helper; 150 | }; 151 | 152 | using rotating_file_sink_mt = rotating_file_sink; 153 | using rotating_file_sink_st = rotating_file_sink; 154 | 155 | /* 156 | * Default generator of daily log file names. 157 | */ 158 | struct default_daily_file_name_calculator 159 | { 160 | // Create filename for the form filename.YYYY-MM-DD_hh-mm.ext 161 | static filename_t calc_filename(const filename_t &filename) 162 | { 163 | std::tm tm = spdlog::details::os::localtime(); 164 | filename_t basename, ext; 165 | std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename); 166 | std::conditional::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; 167 | w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 168 | tm.tm_hour, tm.tm_min, ext); 169 | return w.str(); 170 | } 171 | }; 172 | 173 | /* 174 | * Generator of daily log file names in format basename.YYYY-MM-DD.ext 175 | */ 176 | struct dateonly_daily_file_name_calculator 177 | { 178 | // Create filename for the form basename.YYYY-MM-DD 179 | static filename_t calc_filename(const filename_t &filename) 180 | { 181 | std::tm tm = spdlog::details::os::localtime(); 182 | filename_t basename, ext; 183 | std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename); 184 | std::conditional::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; 185 | w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, ext); 186 | return w.str(); 187 | } 188 | }; 189 | 190 | /* 191 | * Rotating file sink based on date. rotates at midnight 192 | */ 193 | template 194 | class daily_file_sink SPDLOG_FINAL : public base_sink 195 | { 196 | public: 197 | // create daily file sink which rotates on given time 198 | daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute) 199 | : _base_filename(std::move(base_filename)) 200 | , _rotation_h(rotation_hour) 201 | , _rotation_m(rotation_minute) 202 | { 203 | if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59) 204 | { 205 | throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor"); 206 | } 207 | _rotation_tp = _next_rotation_tp(); 208 | _file_helper.open(FileNameCalc::calc_filename(_base_filename)); 209 | } 210 | 211 | protected: 212 | void _sink_it(const details::log_msg &msg) override 213 | { 214 | if (std::chrono::system_clock::now() >= _rotation_tp) 215 | { 216 | _file_helper.open(FileNameCalc::calc_filename(_base_filename)); 217 | _rotation_tp = _next_rotation_tp(); 218 | } 219 | _file_helper.write(msg); 220 | } 221 | 222 | void _flush() override 223 | { 224 | _file_helper.flush(); 225 | } 226 | 227 | private: 228 | std::chrono::system_clock::time_point _next_rotation_tp() 229 | { 230 | auto now = std::chrono::system_clock::now(); 231 | time_t tnow = std::chrono::system_clock::to_time_t(now); 232 | tm date = spdlog::details::os::localtime(tnow); 233 | date.tm_hour = _rotation_h; 234 | date.tm_min = _rotation_m; 235 | date.tm_sec = 0; 236 | auto rotation_time = std::chrono::system_clock::from_time_t(std::mktime(&date)); 237 | if (rotation_time > now) 238 | { 239 | return rotation_time; 240 | } 241 | return {rotation_time + std::chrono::hours(24)}; 242 | } 243 | 244 | filename_t _base_filename; 245 | int _rotation_h; 246 | int _rotation_m; 247 | std::chrono::system_clock::time_point _rotation_tp; 248 | details::file_helper _file_helper; 249 | }; 250 | 251 | using daily_file_sink_mt = daily_file_sink; 252 | using daily_file_sink_st = daily_file_sink; 253 | 254 | } // namespace sinks 255 | } // namespace spdlog 256 | -------------------------------------------------------------------------------- /inc/logger/sinks/msvc_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2016 Alexander Dalshov. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #if defined(_WIN32) 9 | 10 | #include "../details/null_mutex.h" 11 | #include "base_sink.h" 12 | 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | namespace spdlog { 19 | namespace sinks { 20 | /* 21 | * MSVC sink (logging using OutputDebugStringA) 22 | */ 23 | template 24 | class msvc_sink : public base_sink 25 | { 26 | public: 27 | explicit msvc_sink() {} 28 | 29 | protected: 30 | void _sink_it(const details::log_msg &msg) override 31 | { 32 | OutputDebugStringA(msg.formatted.c_str()); 33 | } 34 | 35 | void _flush() override {} 36 | }; 37 | 38 | using msvc_sink_mt = msvc_sink; 39 | using msvc_sink_st = msvc_sink; 40 | 41 | } // namespace sinks 42 | } // namespace spdlog 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /inc/logger/sinks/null_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #include "../details/null_mutex.h" 9 | #include "base_sink.h" 10 | 11 | #include 12 | 13 | namespace spdlog { 14 | namespace sinks { 15 | 16 | template 17 | class null_sink : public base_sink 18 | { 19 | protected: 20 | void _sink_it(const details::log_msg &) override {} 21 | 22 | void _flush() override {} 23 | }; 24 | 25 | using null_sink_mt = null_sink; 26 | using null_sink_st = null_sink; 27 | 28 | } // namespace sinks 29 | } // namespace spdlog 30 | -------------------------------------------------------------------------------- /inc/logger/sinks/ostream_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #include "../details/null_mutex.h" 9 | #include "base_sink.h" 10 | 11 | #include 12 | #include 13 | 14 | namespace spdlog { 15 | namespace sinks { 16 | template 17 | class ostream_sink : public base_sink 18 | { 19 | public: 20 | explicit ostream_sink(std::ostream &os, bool force_flush = false) 21 | : _ostream(os) 22 | , _force_flush(force_flush) 23 | { 24 | } 25 | ostream_sink(const ostream_sink &) = delete; 26 | ostream_sink &operator=(const ostream_sink &) = delete; 27 | 28 | protected: 29 | void _sink_it(const details::log_msg &msg) override 30 | { 31 | _ostream.write(msg.formatted.data(), msg.formatted.size()); 32 | if (_force_flush) 33 | _ostream.flush(); 34 | } 35 | 36 | void _flush() override 37 | { 38 | _ostream.flush(); 39 | } 40 | 41 | std::ostream &_ostream; 42 | bool _force_flush; 43 | }; 44 | 45 | using ostream_sink_mt = ostream_sink; 46 | using ostream_sink_st = ostream_sink; 47 | 48 | } // namespace sinks 49 | } // namespace spdlog 50 | -------------------------------------------------------------------------------- /inc/logger/sinks/sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #include "../details/log_msg.h" 9 | 10 | namespace spdlog { 11 | namespace sinks { 12 | class sink 13 | { 14 | public: 15 | virtual ~sink() = default; 16 | 17 | virtual void log(const details::log_msg &msg) = 0; 18 | virtual void flush() = 0; 19 | 20 | bool should_log(level::level_enum msg_level) const; 21 | void set_level(level::level_enum log_level); 22 | level::level_enum level() const; 23 | 24 | private: 25 | level_t _level{level::trace}; 26 | }; 27 | 28 | inline bool sink::should_log(level::level_enum msg_level) const 29 | { 30 | return msg_level >= _level.load(std::memory_order_relaxed); 31 | } 32 | 33 | inline void sink::set_level(level::level_enum log_level) 34 | { 35 | _level.store(log_level); 36 | } 37 | 38 | inline level::level_enum sink::level() const 39 | { 40 | return static_cast(_level.load(std::memory_order_relaxed)); 41 | } 42 | 43 | } // namespace sinks 44 | } // namespace spdlog 45 | -------------------------------------------------------------------------------- /inc/logger/sinks/stdout_sinks.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #include "../details/null_mutex.h" 9 | #include "base_sink.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace spdlog { 16 | namespace sinks { 17 | 18 | template 19 | class stdout_sink SPDLOG_FINAL : public base_sink 20 | { 21 | using MyType = stdout_sink; 22 | 23 | public: 24 | explicit stdout_sink() = default; 25 | 26 | static std::shared_ptr instance() 27 | { 28 | static std::shared_ptr instance = std::make_shared(); 29 | return instance; 30 | } 31 | 32 | protected: 33 | void _sink_it(const details::log_msg &msg) override 34 | { 35 | fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stdout); 36 | _flush(); 37 | } 38 | 39 | void _flush() override 40 | { 41 | fflush(stdout); 42 | } 43 | }; 44 | 45 | using stdout_sink_mt = stdout_sink; 46 | using stdout_sink_st = stdout_sink; 47 | 48 | template 49 | class stderr_sink SPDLOG_FINAL : public base_sink 50 | { 51 | using MyType = stderr_sink; 52 | 53 | public: 54 | explicit stderr_sink() = default; 55 | 56 | static std::shared_ptr instance() 57 | { 58 | static std::shared_ptr instance = std::make_shared(); 59 | return instance; 60 | } 61 | 62 | protected: 63 | void _sink_it(const details::log_msg &msg) override 64 | { 65 | fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stderr); 66 | _flush(); 67 | } 68 | 69 | void _flush() override 70 | { 71 | fflush(stderr); 72 | } 73 | }; 74 | 75 | using stderr_sink_mt = stderr_sink; 76 | using stderr_sink_st = stderr_sink; 77 | 78 | } // namespace sinks 79 | } // namespace spdlog 80 | -------------------------------------------------------------------------------- /inc/logger/sinks/syslog_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #include "../common.h" 9 | 10 | #ifdef SPDLOG_ENABLE_SYSLOG 11 | 12 | #include "../details/log_msg.h" 13 | #include "sink.h" 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | namespace spdlog { 20 | namespace sinks { 21 | /** 22 | * Sink that write to syslog using the `syscall()` library call. 23 | * 24 | * Locking is not needed, as `syslog()` itself is thread-safe. 25 | */ 26 | class syslog_sink : public sink 27 | { 28 | public: 29 | // 30 | syslog_sink(const std::string &ident = "", int syslog_option = 0, int syslog_facility = LOG_USER) 31 | : _ident(ident) 32 | { 33 | _priorities[static_cast(level::trace)] = LOG_DEBUG; 34 | _priorities[static_cast(level::debug)] = LOG_DEBUG; 35 | _priorities[static_cast(level::info)] = LOG_INFO; 36 | _priorities[static_cast(level::warn)] = LOG_WARNING; 37 | _priorities[static_cast(level::err)] = LOG_ERR; 38 | _priorities[static_cast(level::critical)] = LOG_CRIT; 39 | _priorities[static_cast(level::off)] = LOG_INFO; 40 | 41 | // set ident to be program name if empty 42 | ::openlog(_ident.empty() ? nullptr : _ident.c_str(), syslog_option, syslog_facility); 43 | } 44 | 45 | ~syslog_sink() override 46 | { 47 | ::closelog(); 48 | } 49 | 50 | syslog_sink(const syslog_sink &) = delete; 51 | syslog_sink &operator=(const syslog_sink &) = delete; 52 | 53 | void log(const details::log_msg &msg) override 54 | { 55 | ::syslog(syslog_prio_from_level(msg), "%s", msg.raw.str().c_str()); 56 | } 57 | 58 | void flush() override {} 59 | 60 | private: 61 | std::array _priorities; 62 | // must store the ident because the man says openlog might use the pointer as is and not a string copy 63 | const std::string _ident; 64 | 65 | // 66 | // Simply maps spdlog's log level to syslog priority level. 67 | // 68 | int syslog_prio_from_level(const details::log_msg &msg) const 69 | { 70 | return _priorities[static_cast(msg.level)]; 71 | } 72 | }; 73 | } // namespace sinks 74 | } // namespace spdlog 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /inc/logger/sinks/wincolor_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2016 spdlog 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #include "../common.h" 9 | #include "../details/null_mutex.h" 10 | #include "base_sink.h" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace spdlog { 18 | namespace sinks { 19 | /* 20 | * Windows color console sink. Uses WriteConsoleA to write to the console with colors 21 | */ 22 | template 23 | class wincolor_sink : public base_sink 24 | { 25 | public: 26 | const WORD BOLD = FOREGROUND_INTENSITY; 27 | const WORD RED = FOREGROUND_RED; 28 | const WORD GREEN = FOREGROUND_GREEN; 29 | const WORD CYAN = FOREGROUND_GREEN | FOREGROUND_BLUE; 30 | const WORD WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; 31 | const WORD YELLOW = FOREGROUND_RED | FOREGROUND_GREEN; 32 | 33 | wincolor_sink(HANDLE std_handle) 34 | : out_handle_(std_handle) 35 | { 36 | colors_[level::trace] = WHITE; 37 | colors_[level::debug] = CYAN; 38 | colors_[level::info] = GREEN; 39 | colors_[level::warn] = YELLOW | BOLD; 40 | colors_[level::err] = RED | BOLD; // red bold 41 | colors_[level::critical] = BACKGROUND_RED | WHITE | BOLD; // white bold on red background 42 | colors_[level::off] = 0; 43 | } 44 | 45 | ~wincolor_sink() override 46 | { 47 | this->flush(); 48 | } 49 | 50 | wincolor_sink(const wincolor_sink &other) = delete; 51 | wincolor_sink &operator=(const wincolor_sink &other) = delete; 52 | 53 | // change the color for the given level 54 | void set_color(level::level_enum level, WORD color) 55 | { 56 | std::lock_guard lock(base_sink::_mutex); 57 | colors_[level] = color; 58 | } 59 | 60 | protected: 61 | void _sink_it(const details::log_msg &msg) override 62 | { 63 | if (msg.color_range_end > msg.color_range_start) 64 | { 65 | // before color range 66 | _print_range(msg, 0, msg.color_range_start); 67 | 68 | // in color range 69 | auto orig_attribs = set_console_attribs(colors_[msg.level]); 70 | _print_range(msg, msg.color_range_start, msg.color_range_end); 71 | ::SetConsoleTextAttribute(out_handle_, orig_attribs); // reset to orig colors 72 | // after color range 73 | _print_range(msg, msg.color_range_end, msg.formatted.size()); 74 | } 75 | else // print without colors if color range is invalid 76 | { 77 | _print_range(msg, 0, msg.formatted.size()); 78 | } 79 | } 80 | 81 | void _flush() override 82 | { 83 | // windows console always flushed? 84 | } 85 | 86 | private: 87 | HANDLE out_handle_; 88 | std::unordered_map colors_; 89 | 90 | // set color and return the orig console attributes (for resetting later) 91 | WORD set_console_attribs(WORD attribs) 92 | { 93 | CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info; 94 | GetConsoleScreenBufferInfo(out_handle_, &orig_buffer_info); 95 | WORD back_color = orig_buffer_info.wAttributes; 96 | // retrieve the current background color 97 | back_color &= static_cast(~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY)); 98 | // keep the background color unchanged 99 | SetConsoleTextAttribute(out_handle_, attribs | back_color); 100 | return orig_buffer_info.wAttributes; // return orig attribs 101 | } 102 | 103 | // print a range of formatted message to console 104 | void _print_range(const details::log_msg &msg, size_t start, size_t end) 105 | { 106 | DWORD size = static_cast(end - start); 107 | WriteConsoleA(out_handle_, msg.formatted.data() + start, size, nullptr, nullptr); 108 | } 109 | }; 110 | 111 | // 112 | // windows color console to stdout 113 | // 114 | template 115 | class wincolor_stdout_sink : public wincolor_sink 116 | { 117 | public: 118 | wincolor_stdout_sink() 119 | : wincolor_sink(GetStdHandle(STD_OUTPUT_HANDLE)) 120 | { 121 | } 122 | }; 123 | 124 | using wincolor_stdout_sink_mt = wincolor_stdout_sink; 125 | using wincolor_stdout_sink_st = wincolor_stdout_sink; 126 | 127 | // 128 | // windows color console to stderr 129 | // 130 | template 131 | class wincolor_stderr_sink : public wincolor_sink 132 | { 133 | public: 134 | wincolor_stderr_sink() 135 | : wincolor_sink(GetStdHandle(STD_ERROR_HANDLE)) 136 | { 137 | } 138 | }; 139 | 140 | using wincolor_stderr_sink_mt = wincolor_stderr_sink; 141 | using wincolor_stderr_sink_st = wincolor_stderr_sink; 142 | 143 | } // namespace sinks 144 | } // namespace spdlog 145 | -------------------------------------------------------------------------------- /inc/logger/sinks/windebug_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2017 Alexander Dalshov. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #if defined(_WIN32) 9 | 10 | #include "msvc_sink.h" 11 | 12 | namespace spdlog { 13 | namespace sinks { 14 | 15 | /* 16 | * Windows debug sink (logging using OutputDebugStringA, synonym for msvc_sink) 17 | */ 18 | template 19 | using windebug_sink = msvc_sink; 20 | 21 | using windebug_sink_mt = msvc_sink_mt; 22 | using windebug_sink_st = msvc_sink_st; 23 | 24 | } // namespace sinks 25 | } // namespace spdlog 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /inc/logger/spdlog.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | // spdlog main header file. 6 | // see example.cpp for usage example 7 | 8 | #pragma once 9 | 10 | #include "common.h" 11 | #include "logger.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace spdlog { 19 | 20 | // 21 | // Return an existing logger or nullptr if a logger with such name doesn't exist. 22 | // example: spdlog::get("my_logger")->info("hello {}", "world"); 23 | // 24 | std::shared_ptr get(const std::string &name); 25 | 26 | // 27 | // Set global formatting 28 | // example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v"); 29 | // 30 | void set_pattern(const std::string &format_string); 31 | void set_formatter(formatter_ptr f); 32 | 33 | // 34 | // Set global logging level 35 | // 36 | void set_level(level::level_enum log_level); 37 | 38 | // 39 | // Set global flush level 40 | // 41 | void flush_on(level::level_enum log_level); 42 | 43 | // 44 | // Set global error handler 45 | // 46 | void set_error_handler(log_err_handler handler); 47 | 48 | // 49 | // Turn on async mode (off by default) and set the queue size for each async_logger. 50 | // effective only for loggers created after this call. 51 | // queue_size: size of queue (must be power of 2): 52 | // Each logger will pre-allocate a dedicated queue with queue_size entries upon construction. 53 | // 54 | // async_overflow_policy (optional, block_retry by default): 55 | // async_overflow_policy::block_retry - if queue is full, block until queue has room for the new log entry. 56 | // async_overflow_policy::discard_log_msg - never block and discard any new messages when queue overflows. 57 | // 58 | // worker_warmup_cb (optional): 59 | // callback function that will be called in worker thread upon start (can be used to init stuff like thread affinity) 60 | // 61 | // worker_teardown_cb (optional): 62 | // callback function that will be called in worker thread upon exit 63 | // 64 | void set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, 65 | const std::function &worker_warmup_cb = nullptr, 66 | const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(), 67 | const std::function &worker_teardown_cb = nullptr); 68 | 69 | // Turn off async mode 70 | void set_sync_mode(); 71 | 72 | // 73 | // Create and register multi/single threaded basic file logger. 74 | // Basic logger simply writes to given file without any limitations or rotations. 75 | // 76 | std::shared_ptr basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false); 77 | std::shared_ptr basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false); 78 | 79 | // 80 | // Create and register multi/single threaded rotating file logger 81 | // 82 | std::shared_ptr rotating_logger_mt( 83 | const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files); 84 | 85 | std::shared_ptr rotating_logger_st( 86 | const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files); 87 | 88 | // 89 | // Create file logger which creates new file on the given time (default in midnight): 90 | // 91 | std::shared_ptr daily_logger_mt(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0); 92 | std::shared_ptr daily_logger_st(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0); 93 | 94 | // 95 | // Create and register stdout/stderr loggers 96 | // 97 | std::shared_ptr stdout_logger_mt(const std::string &logger_name); 98 | std::shared_ptr stdout_logger_st(const std::string &logger_name); 99 | std::shared_ptr stderr_logger_mt(const std::string &logger_name); 100 | std::shared_ptr stderr_logger_st(const std::string &logger_name); 101 | // 102 | // Create and register colored stdout/stderr loggers 103 | // 104 | std::shared_ptr stdout_color_mt(const std::string &logger_name); 105 | std::shared_ptr stdout_color_st(const std::string &logger_name); 106 | std::shared_ptr stderr_color_mt(const std::string &logger_name); 107 | std::shared_ptr stderr_color_st(const std::string &logger_name); 108 | 109 | // 110 | // Create and register a syslog logger 111 | // 112 | #ifdef SPDLOG_ENABLE_SYSLOG 113 | std::shared_ptr syslog_logger( 114 | const std::string &logger_name, const std::string &ident = "", int syslog_option = 0, int syslog_facilty = (1 << 3)); 115 | #endif 116 | 117 | #if defined(__ANDROID__) 118 | std::shared_ptr android_logger(const std::string &logger_name, const std::string &tag = "spdlog"); 119 | #endif 120 | 121 | // Create and register a logger with a single sink 122 | std::shared_ptr create(const std::string &logger_name, const sink_ptr &sink); 123 | 124 | // Create and register a logger with multiple sinks 125 | std::shared_ptr create(const std::string &logger_name, sinks_init_list sinks); 126 | 127 | template 128 | std::shared_ptr create(const std::string &logger_name, const It &sinks_begin, const It &sinks_end); 129 | 130 | // Create and register a logger with templated sink type 131 | // Example: 132 | // spdlog::create("mylog", "dailylog_filename"); 133 | template 134 | std::shared_ptr create(const std::string &logger_name, Args... args); 135 | 136 | // Create and register an async logger with a single sink 137 | std::shared_ptr create_async(const std::string &logger_name, const sink_ptr &sink, size_t queue_size, 138 | const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, 139 | const std::function &worker_warmup_cb = nullptr, 140 | const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(), 141 | const std::function &worker_teardown_cb = nullptr); 142 | 143 | // Create and register an async logger with multiple sinks 144 | std::shared_ptr create_async(const std::string &logger_name, sinks_init_list sinks, size_t queue_size, 145 | const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, 146 | const std::function &worker_warmup_cb = nullptr, 147 | const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(), 148 | const std::function &worker_teardown_cb = nullptr); 149 | 150 | template 151 | std::shared_ptr create_async(const std::string &logger_name, const It &sinks_begin, const It &sinks_end, size_t queue_size, 152 | const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, 153 | const std::function &worker_warmup_cb = nullptr, 154 | const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(), 155 | const std::function &worker_teardown_cb = nullptr); 156 | 157 | // Register the given logger with the given name 158 | void register_logger(std::shared_ptr logger); 159 | 160 | // Apply a user defined function on all registered loggers 161 | // Example: 162 | // spdlog::apply_all([&](std::shared_ptr l) {l->flush();}); 163 | void apply_all(std::function)> fun); 164 | 165 | // Drop the reference to the given logger 166 | void drop(const std::string &name); 167 | 168 | // Drop all references from the registry 169 | void drop_all(); 170 | 171 | /////////////////////////////////////////////////////////////////////////////// 172 | // 173 | // Trace & Debug can be switched on/off at compile time for zero cost debug statements. 174 | // Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in tweakme.h to enable. 175 | // SPDLOG_TRACE(..) will also print current file and line. 176 | // 177 | // Example: 178 | // spdlog::set_level(spdlog::level::trace); 179 | // SPDLOG_TRACE(my_logger, "some trace message"); 180 | // SPDLOG_TRACE(my_logger, "another trace message {} {}", 1, 2); 181 | // SPDLOG_DEBUG(my_logger, "some debug message {} {}", 3, 4); 182 | /////////////////////////////////////////////////////////////////////////////// 183 | 184 | #ifdef SPDLOG_TRACE_ON 185 | #define SPDLOG_STR_H(x) #x 186 | #define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x) 187 | #ifdef _MSC_VER 188 | #define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ "(" SPDLOG_STR_HELPER(__LINE__) ") ] " __VA_ARGS__) 189 | #else 190 | #define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ ":" SPDLOG_STR_HELPER(__LINE__) " ] " __VA_ARGS__) 191 | #endif 192 | #else 193 | #define SPDLOG_TRACE(logger, ...) (void)0 194 | #endif 195 | 196 | #ifdef SPDLOG_DEBUG_ON 197 | #define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__) 198 | #else 199 | #define SPDLOG_DEBUG(logger, ...) (void)0 200 | #endif 201 | 202 | } // namespace spdlog 203 | 204 | #include "details/spdlog_impl.h" 205 | -------------------------------------------------------------------------------- /inc/logger/tweakme.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | /////////////////////////////////////////////////////////////////////////////// 9 | // 10 | // Edit this file to squeeze more performance, and to customize supported features 11 | // 12 | /////////////////////////////////////////////////////////////////////////////// 13 | 14 | /////////////////////////////////////////////////////////////////////////////// 15 | // Under Linux, the much faster CLOCK_REALTIME_COARSE clock can be used. 16 | // This clock is less accurate - can be off by dozens of millis - depending on the kernel HZ. 17 | // Uncomment to use it instead of the regular clock. 18 | // 19 | // #define SPDLOG_CLOCK_COARSE 20 | /////////////////////////////////////////////////////////////////////////////// 21 | 22 | /////////////////////////////////////////////////////////////////////////////// 23 | // Uncomment if date/time logging is not needed and never appear in the log pattern. 24 | // This will prevent spdlog from querying the clock on each log call. 25 | // 26 | // WARNING: If the log pattern contains any date/time while this flag is on, the result is undefined. 27 | // You must set new pattern(spdlog::set_pattern(..") without any date/time in it 28 | // 29 | // #define SPDLOG_NO_DATETIME 30 | /////////////////////////////////////////////////////////////////////////////// 31 | 32 | /////////////////////////////////////////////////////////////////////////////// 33 | // Uncomment if thread id logging is not needed (i.e. no %t in the log pattern). 34 | // This will prevent spdlog from querying the thread id on each log call. 35 | // 36 | // WARNING: If the log pattern contains thread id (i.e, %t) while this flag is on, the result is undefined. 37 | // 38 | // #define SPDLOG_NO_THREAD_ID 39 | /////////////////////////////////////////////////////////////////////////////// 40 | 41 | /////////////////////////////////////////////////////////////////////////////// 42 | // Uncomment to prevent spdlog from caching thread ids in thread local storage. 43 | // By default spdlog saves thread ids in tls to gain a few micros for each call. 44 | // 45 | // WARNING: if your program forks, UNCOMMENT this flag to prevent undefined thread ids in the children logs. 46 | // 47 | // #define SPDLOG_DISABLE_TID_CACHING 48 | /////////////////////////////////////////////////////////////////////////////// 49 | 50 | /////////////////////////////////////////////////////////////////////////////// 51 | // Uncomment if logger name logging is not needed. 52 | // This will prevent spdlog from copying the logger name on each log call. 53 | // 54 | // #define SPDLOG_NO_NAME 55 | /////////////////////////////////////////////////////////////////////////////// 56 | 57 | /////////////////////////////////////////////////////////////////////////////// 58 | // Uncomment to enable the SPDLOG_DEBUG/SPDLOG_TRACE macros. 59 | // 60 | // #define SPDLOG_DEBUG_ON 61 | // #define SPDLOG_TRACE_ON 62 | /////////////////////////////////////////////////////////////////////////////// 63 | 64 | /////////////////////////////////////////////////////////////////////////////// 65 | // Uncomment to avoid locking in the registry operations (spdlog::get(), spdlog::drop() spdlog::register()). 66 | // Use only if your code never modifies concurrently the registry. 67 | // Note that upon creating a logger the registry is modified by spdlog.. 68 | // 69 | // #define SPDLOG_NO_REGISTRY_MUTEX 70 | /////////////////////////////////////////////////////////////////////////////// 71 | 72 | /////////////////////////////////////////////////////////////////////////////// 73 | // Uncomment to avoid spdlog's usage of atomic log levels 74 | // Use only if your code never modifies a logger's log levels concurrently by different threads. 75 | // 76 | // #define SPDLOG_NO_ATOMIC_LEVELS 77 | /////////////////////////////////////////////////////////////////////////////// 78 | 79 | /////////////////////////////////////////////////////////////////////////////// 80 | // Uncomment to enable usage of wchar_t for file names on Windows. 81 | // 82 | // #define SPDLOG_WCHAR_FILENAMES 83 | /////////////////////////////////////////////////////////////////////////////// 84 | 85 | /////////////////////////////////////////////////////////////////////////////// 86 | // Uncomment to override default eol ("\n" or "\r\n" under Linux/Windows) 87 | // 88 | // #define SPDLOG_EOL ";-)\n" 89 | /////////////////////////////////////////////////////////////////////////////// 90 | 91 | /////////////////////////////////////////////////////////////////////////////// 92 | // Uncomment to use your own copy of the fmt library instead of spdlog's copy. 93 | // In this case spdlog will try to include so set your -I flag accordingly. 94 | // 95 | // #define SPDLOG_FMT_EXTERNAL 96 | /////////////////////////////////////////////////////////////////////////////// 97 | 98 | /////////////////////////////////////////////////////////////////////////////// 99 | // Uncomment to use printf-style messages in your logs instead of the usual 100 | // format-style used by default. 101 | // 102 | // #define SPDLOG_FMT_PRINTF 103 | /////////////////////////////////////////////////////////////////////////////// 104 | 105 | /////////////////////////////////////////////////////////////////////////////// 106 | // Uncomment to enable syslog (disabled by default) 107 | // 108 | // #define SPDLOG_ENABLE_SYSLOG 109 | /////////////////////////////////////////////////////////////////////////////// 110 | 111 | /////////////////////////////////////////////////////////////////////////////// 112 | // Uncomment to enable wchar_t support (convert to utf8) 113 | // 114 | // #define SPDLOG_WCHAR_TO_UTF8_SUPPORT 115 | /////////////////////////////////////////////////////////////////////////////// 116 | 117 | /////////////////////////////////////////////////////////////////////////////// 118 | // Uncomment to prevent child processes from inheriting log file descriptors 119 | // 120 | // #define SPDLOG_PREVENT_CHILD_FD 121 | /////////////////////////////////////////////////////////////////////////////// 122 | 123 | /////////////////////////////////////////////////////////////////////////////// 124 | // Uncomment if your compiler doesn't support the "final" keyword. 125 | // The final keyword allows more optimizations in release 126 | // mode with recent compilers. See GCC's documentation for -Wsuggest-final-types 127 | // for instance. 128 | // 129 | // #define SPDLOG_NO_FINAL 130 | /////////////////////////////////////////////////////////////////////////////// 131 | 132 | /////////////////////////////////////////////////////////////////////////////// 133 | // Uncomment to enable message counting feature. 134 | // Use the %i in the logger pattern to display log message sequence id. 135 | // 136 | // #define SPDLOG_ENABLE_MESSAGE_COUNTER 137 | /////////////////////////////////////////////////////////////////////////////// 138 | 139 | /////////////////////////////////////////////////////////////////////////////// 140 | // Uncomment to customize level names (e.g. "MT TRACE") 141 | // 142 | // #define SPDLOG_LEVEL_NAMES { "MY TRACE", "MY DEBUG", "MY INFO", "MY WARNING", "MY ERROR", "MY CRITICAL", "OFF" } 143 | /////////////////////////////////////////////////////////////////////////////// 144 | -------------------------------------------------------------------------------- /java/screen_recorder/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | aparfenov 4 | screen_recorder 5 | 0.0.1-SNAPSHOT 6 | screen_recorder 7 | java binding for screen recorder library 8 | https://github.com/Andrey1994/screen_recorder_sdk 9 | 10 | 11 | 12 | MIT 13 | https://github.com/Andrey1994/screen_recorder_sdk/blob/master/LICENSE 14 | repo 15 | 16 | 17 | 18 | 19 | 20 | github 21 | GitHub Andrey1994 Apache Maven Packages 22 | https://maven.pkg.github.com/Andrey1994/screen_recorder_sdk 23 | 24 | 25 | 26 | 27 | screen_recorder 28 | 29 | 30 | 31 | ${project.build.outputDirectory}/${project.build.finalName} 32 | false 33 | ${basedir}/src/main/resources 34 | 35 | ScreenRecorder.dll 36 | 37 | 38 | 39 | 40 | 41 | 42 | org.apache.maven.plugins 43 | maven-compiler-plugin 44 | 3.8.0 45 | 46 | 11 47 | 48 | 49 | 50 | org.apache.maven.plugins 51 | maven-dependency-plugin 52 | 53 | 54 | copy-dependencies 55 | prepare-package 56 | 57 | copy-dependencies 58 | 59 | 60 | 61 | ${project.build.directory}/libs 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | org.apache.maven.plugins 70 | maven-assembly-plugin 71 | 72 | 73 | package 74 | 75 | single 76 | 77 | 78 | 79 | jar-with-dependencies 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | pl.project13.maven 88 | git-commit-id-plugin 89 | 2.2.4 90 | 91 | 92 | get-the-git-infos 93 | 94 | revision 95 | 96 | 97 | 98 | 99 | ${project.basedir}/.git 100 | git 101 | false 102 | true 103 | ${project.build.outputDirectory}/git.properties 104 | json 105 | 106 | false 107 | false 108 | -dirty 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | net.java.dev.jna 119 | jna-platform 120 | 4.5.1 121 | 122 | 123 | org.apache.commons 124 | commons-lang3 125 | 3.1 126 | 127 | 128 | org.apache.commons 129 | commons-math3 130 | 3.1 131 | 132 | 133 | com.google.code.gson 134 | gson 135 | 2.3.1 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /java/screen_recorder/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | github 10 | USER 11 | TOKEN 12 | 13 | 14 | 15 | 16 | 17 | github 18 | 19 | 20 | 21 | central 22 | https://repo1.maven.org/maven2 23 | 24 | true 25 | 26 | 27 | true 28 | 29 | 30 | 31 | 32 | github 33 | GitHub screen recorder Apache Maven Packages 34 | https://maven.pkg.github.com/Andrey1994/screen_recorder_sdk 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | github 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /java/screen_recorder/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module screen_recorder 2 | { 3 | exports screen_recorder; 4 | 5 | requires commons.lang3; 6 | requires gson; 7 | requires java.desktop; 8 | requires jna; 9 | } 10 | -------------------------------------------------------------------------------- /java/screen_recorder/src/main/java/screen_recorder/ExitCode.java: -------------------------------------------------------------------------------- 1 | package screen_recorder; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public enum ExitCode 7 | { 8 | 9 | STATUS_OK (0), 10 | NO_SUCH_PROCESS_ERROR (100), 11 | RECORDING_ALREADY_RUN_ERROR (101), 12 | RECORDING_THREAD_ERROR (102), 13 | RECORDING_THREAD_IS_NOT_RUNNING_ERROR (103), 14 | INVALID_ARGUMENTS_ERROR (104), 15 | SESSION_NOT_CREATED_ERROR (105), 16 | PREPARE_DESK_DUPL_ERROR (106), 17 | CREATE_TEXTURE_ERROR (107), 18 | DDA_CAPTURE_ERROR (108), 19 | FIND_WINDOW_ERROR (109), 20 | DDA_LOST_ACCESS_ERROR (110), 21 | DDA_TIMEOUT_ERROR (111), 22 | SYNC_TIMEOUT_ERROR (112), 23 | GENERAL_ERROR (113); 24 | 25 | private final int exit_code; 26 | private static final Map ec_map = new HashMap (); 27 | 28 | public int get_code () 29 | { 30 | return exit_code; 31 | } 32 | 33 | public static String string_from_code (final int code) 34 | { 35 | return from_code (code).name (); 36 | } 37 | 38 | public static ExitCode from_code (final int code) 39 | { 40 | final ExitCode element = ec_map.get (code); 41 | return element; 42 | } 43 | 44 | ExitCode (final int code) 45 | { 46 | exit_code = code; 47 | } 48 | 49 | static 50 | { 51 | for (final ExitCode ec : ExitCode.values ()) 52 | { 53 | ec_map.put (ec.get_code (), ec); 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /java/screen_recorder/src/main/java/screen_recorder/RecorderError.java: -------------------------------------------------------------------------------- 1 | package screen_recorder; 2 | 3 | /** 4 | * RecorderError exception to notify about errors 5 | */ 6 | public class RecorderError extends Exception 7 | { 8 | public String msg; 9 | /** 10 | * exit code returned from low level API 11 | */ 12 | public int exit_code; 13 | 14 | public RecorderError (String message, int ec) 15 | { 16 | super (message + ":" + screen_recorder.ExitCode.string_from_code (ec)); 17 | exit_code = ec; 18 | msg = message; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /java/screen_recorder/src/main/java/screen_recorder/RecorderParams.java: -------------------------------------------------------------------------------- 1 | package screen_recorder; 2 | 3 | import com.google.gson.Gson; 4 | 5 | public class RecorderParams 6 | { 7 | public int desktop_num; 8 | public int pid; 9 | 10 | public RecorderParams () 11 | { 12 | desktop_num = 0; 13 | pid = 0; 14 | } 15 | 16 | public String to_json () 17 | { 18 | return new Gson ().toJson (this); 19 | } 20 | 21 | public int get_desktop_num () 22 | { 23 | return desktop_num; 24 | } 25 | 26 | public void set_desktop_num (int desktop_num) 27 | { 28 | this.desktop_num = desktop_num; 29 | } 30 | 31 | public int get_pid () 32 | { 33 | return pid; 34 | } 35 | 36 | public void set_pid (int pid) 37 | { 38 | this.pid = pid; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /java/screen_recorder/src/main/java/screen_recorder/ScreenRecorder.java: -------------------------------------------------------------------------------- 1 | package screen_recorder; 2 | 3 | import java.awt.Color; 4 | import java.awt.Image; 5 | import java.awt.image.BufferedImage; 6 | import java.io.File; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.nio.file.Files; 10 | import java.nio.file.Path; 11 | import java.util.Arrays; 12 | 13 | import org.apache.commons.lang3.SystemUtils; 14 | 15 | import com.sun.jna.Library; 16 | import com.sun.jna.Native; 17 | 18 | /** 19 | * Class to record video and take screenshots 20 | */ 21 | public class ScreenRecorder 22 | { 23 | 24 | private interface DllInterface extends Library 25 | { 26 | int InitResources (String params); 27 | 28 | int GetScreenShot (int max_attempts, byte[] framebuffer, int[] width, int[] height); 29 | 30 | int StartVideoRecording (String filename, int frameRate, int bitRate, int useHardwareTransform); 31 | 32 | int StopVideoRecording (); 33 | 34 | int FreeResources (); 35 | 36 | int GetPID (int[] pid); 37 | 38 | int SetLogLevel (int log_level); 39 | 40 | int SetLogFile (String log_file); 41 | } 42 | 43 | private static DllInterface instance; 44 | 45 | static 46 | { 47 | 48 | String lib_name = "ScreenRecorder.dll"; 49 | // need to extract libraries from jar 50 | unpack_from_jar (lib_name); 51 | instance = (DllInterface) Native.loadLibrary (lib_name, DllInterface.class); 52 | } 53 | 54 | private static Path unpack_from_jar (String lib_name) 55 | { 56 | try 57 | { 58 | File file = new File (lib_name); 59 | if (file.exists ()) 60 | file.delete (); 61 | InputStream link = (ScreenRecorder.class.getResourceAsStream (lib_name)); 62 | Files.copy (link, file.getAbsoluteFile ().toPath ()); 63 | return file.getAbsoluteFile ().toPath (); 64 | } catch (Exception io) 65 | { 66 | System.err.println ("file: " + lib_name + " is not found in jar file"); 67 | return null; 68 | } 69 | } 70 | 71 | /** 72 | * enable logger with level INFO 73 | */ 74 | public static void enable_log () throws RecorderError 75 | { 76 | set_log_level (2); 77 | } 78 | 79 | /** 80 | * enable logger with level TRACE 81 | */ 82 | public static void enable_dev_log () throws RecorderError 83 | { 84 | set_log_level (0); 85 | } 86 | 87 | /** 88 | * disable logger 89 | */ 90 | public static void disable_log () throws RecorderError 91 | { 92 | set_log_level (6); 93 | } 94 | 95 | /** 96 | * redirect logger from stderr to a file 97 | */ 98 | public static void set_log_file (String log_file) throws RecorderError 99 | { 100 | int ec = instance.SetLogFile (log_file); 101 | if (ec != ExitCode.STATUS_OK.get_code ()) 102 | { 103 | throw new RecorderError ("Error in SetLogFile", ec); 104 | } 105 | } 106 | 107 | /** 108 | * set log level 109 | */ 110 | public static void set_log_level (int log_level) throws RecorderError 111 | { 112 | int ec = instance.SetLogLevel (log_level); 113 | if (ec != ExitCode.STATUS_OK.get_code ()) 114 | { 115 | throw new RecorderError ("Error in set_log_level", ec); 116 | } 117 | } 118 | 119 | private String input_json; 120 | 121 | /** 122 | * Create ScreenRecorder object 123 | */ 124 | public ScreenRecorder (RecorderParams params) 125 | { 126 | this.input_json = params.to_json (); 127 | } 128 | 129 | /** 130 | * prepare required resources, creates worker thread 131 | */ 132 | public void init_resources () throws RecorderError 133 | { 134 | int ec = instance.InitResources (input_json); 135 | if (ec != ExitCode.STATUS_OK.get_code ()) 136 | { 137 | throw new RecorderError ("Error in init_resources", ec); 138 | } 139 | // workaround black screenshot if called right after init_resources, dont see 140 | // race cond in C++ and can not reproduce in python 141 | try 142 | { 143 | Thread.sleep (100); 144 | } catch (InterruptedException e) 145 | { 146 | } 147 | } 148 | 149 | /** 150 | * free all resources 151 | */ 152 | public void free_resources () throws RecorderError 153 | { 154 | int ec = instance.FreeResources (); 155 | if (ec != ExitCode.STATUS_OK.get_code ()) 156 | { 157 | throw new RecorderError ("Error in free_resources", ec); 158 | } 159 | } 160 | 161 | /** 162 | * get pid 163 | */ 164 | public int get_pid () throws RecorderError 165 | { 166 | int[] res = new int[1]; 167 | int ec = instance.GetPID (res); 168 | if (ec != ExitCode.STATUS_OK.get_code ()) 169 | { 170 | throw new RecorderError ("Error in get_pid", ec); 171 | } 172 | return res[0]; 173 | } 174 | 175 | /** 176 | * start video recording 177 | */ 178 | public void start_video_recording (String filename, int frame_rate, int bit_rate, boolean use_hw_transfowrms) 179 | throws RecorderError 180 | { 181 | int ec = instance.StartVideoRecording (filename, frame_rate, bit_rate, use_hw_transfowrms ? 1 : 0); 182 | if (ec != ExitCode.STATUS_OK.get_code ()) 183 | { 184 | throw new RecorderError ("Error in start_video_recording", ec); 185 | } 186 | } 187 | 188 | /** 189 | * start video recording with default args 190 | */ 191 | public void start_video_recording (String filename) throws RecorderError 192 | { 193 | start_video_recording (filename, 30, 8000000, true); 194 | } 195 | 196 | /** 197 | * stop video recording 198 | */ 199 | public void stop_video_recording () throws RecorderError 200 | { 201 | int ec = instance.StopVideoRecording (); 202 | if (ec != ExitCode.STATUS_OK.get_code ()) 203 | { 204 | throw new RecorderError ("Error in stop_video_recording", ec); 205 | } 206 | } 207 | 208 | /** 209 | * get screenshot 210 | */ 211 | public BufferedImage get_screenshot (int max_attempts) throws RecorderError 212 | { 213 | int max_width = 4096; 214 | int max_height = 4096; 215 | int max_pixels = max_width * max_height; 216 | byte[] framebuffer = new byte[max_pixels]; 217 | int[] width = new int[1]; 218 | int[] height = new int[1]; 219 | int ec = instance.GetScreenShot (max_attempts, framebuffer, width, height); 220 | if (ec != ExitCode.STATUS_OK.get_code ()) 221 | { 222 | throw new RecorderError ("Error in start_video_recording", ec); 223 | } 224 | 225 | BufferedImage img = new BufferedImage (width[0], height[0], BufferedImage.TYPE_INT_ARGB); 226 | int index = 0; 227 | for (int r = 0; r < height[0]; r++) 228 | { 229 | for (int c = 0; c < width[0]; c++) 230 | { 231 | int red = ((int) framebuffer[index + 2]) & 0xFF; 232 | int green = ((int) framebuffer[index + 1]) & 0xFF; 233 | int blue = ((int) framebuffer[index]) & 0xFF; 234 | Color color = new Color (red, green, blue); 235 | img.setRGB (c, r, color.getRGB ()); 236 | index += 4; 237 | } 238 | } 239 | return img; 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /java/screen_recorder/src/main/resources/ScreenRecorder.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andrey1994/screen_recorder_sdk/31417c8af136a7b8b44702e69fa0bb6ebb5c2b13/java/screen_recorder/src/main/resources/ScreenRecorder.dll -------------------------------------------------------------------------------- /java/screen_recorder/target/screen_recorder-jar-with-dependencies.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andrey1994/screen_recorder_sdk/31417c8af136a7b8b44702e69fa0bb6ebb5c2b13/java/screen_recorder/target/screen_recorder-jar-with-dependencies.jar -------------------------------------------------------------------------------- /java/screen_recorder/target/screen_recorder.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andrey1994/screen_recorder_sdk/31417c8af136a7b8b44702e69fa0bb6ebb5c2b13/java/screen_recorder/target/screen_recorder.jar -------------------------------------------------------------------------------- /python/README.md: -------------------------------------------------------------------------------- 1 | # Screen Recorder SDK 2 | Library to take screenshots and record videos 3 | 4 | I use [Desktop Duplication API](https://docs.microsoft.com/en-us/windows/desktop/direct3ddxgi/desktop-dup-api) to capture desktop and [Media Foundation API](https://docs.microsoft.com/en-us/windows/desktop/medfound/media-foundation-platform-apis) to record video. 5 | 6 | For screenshots it cuts process window from desktop while for videos it captures full display without cutting for better performance 7 | 8 | ## System Requirements 9 | 10 | * Windows >= 10, it may work on Windows 8.1 and Windows Server 2012, but we don't ensure it 11 | * DirectX, you can install it from [Microsoft Website](https://www.microsoft.com/en-us/download/details.aspx?id=17431) 12 | * Media Feature Pack, download it [here](https://www.microsoft.com/en-us/software-download/mediafeaturepack) 13 | * 64 bits Java or Python, we don't provide x86 libs 14 | 15 | ## Installation 16 | 17 | First option is: 18 | ``` 19 | git clone https://github.com/Andrey1994/screen_recorder_sdk 20 | cd screen_recorder_sdk 21 | cd python 22 | pip install . 23 | ``` 24 | ALso you can install it from PYPI: 25 | ``` 26 | pip install screen_recorder_sdk 27 | ``` 28 | 29 | ## Simple Sample 30 | 31 | ``` 32 | import sys 33 | import time 34 | import numpy 35 | 36 | from screen_recorder_sdk import screen_recorder 37 | 38 | 39 | def main (): 40 | screen_recorder.enable_dev_log () 41 | pid = int (sys.argv[1]) # pid == 0 means capture full screen 42 | screen_recorder.init_resources (pid) 43 | 44 | screen_recorder.get_screenshot (5).save ('test_before.png') 45 | 46 | screen_recorder.start_video_recording ('video1.mp4', 30, 8000000, True) 47 | time.sleep (5) 48 | screen_recorder.get_screenshot (5).save ('test_during_video.png') 49 | time.sleep (5) 50 | screen_recorder.stop_video_recording () 51 | 52 | screen_recorder.start_video_recording ('video2.mp4', 30, 8000000, True) 53 | time.sleep (5) 54 | screen_recorder.stop_video_recording () 55 | 56 | screen_recorder.free_resources () 57 | 58 | if __name__ == "__main__": 59 | main () 60 | ``` 61 | 62 | [More samples](https://github.com/Andrey1994/screen_recorder_sdk/tree/master/python/example) 63 | -------------------------------------------------------------------------------- /python/example/recorder_simple_sample.py: -------------------------------------------------------------------------------- 1 | import time 2 | import numpy 3 | 4 | from screen_recorder_sdk import screen_recorder 5 | 6 | 7 | def main (): 8 | screen_recorder.enable_dev_log () 9 | 10 | params = screen_recorder.RecorderParams () 11 | # params.pid = 0 # use it to set process Id to capture 12 | # params.desktop_num = 0 # use it to set desktop num, counting from 0 13 | 14 | screen_recorder.init_resources (params) 15 | 16 | screen_recorder.get_screenshot (5).save ('test_before.png') 17 | 18 | screen_recorder.start_video_recording ('video1.mp4', 30, 8000000, True) 19 | time.sleep (5) 20 | screen_recorder.get_screenshot (5).save ('test_during_video.png') 21 | time.sleep (5) 22 | screen_recorder.stop_video_recording () 23 | 24 | screen_recorder.start_video_recording ('video2.mp4', 30, 8000000, True) 25 | time.sleep (5) 26 | screen_recorder.stop_video_recording () 27 | 28 | screen_recorder.free_resources () 29 | 30 | if __name__ == "__main__": 31 | main () 32 | -------------------------------------------------------------------------------- /python/example/recorder_threaded_sample.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | import numpy 4 | import threading 5 | 6 | from screen_recorder_sdk import screen_recorder 7 | 8 | class ScreenShoter (threading.Thread): 9 | 10 | def __init__ (self): 11 | threading.Thread.__init__ (self) 12 | self.should_stop = False 13 | 14 | def run (self): 15 | i = 0 16 | while not self.should_stop: 17 | screen_recorder.get_screenshot (5).save ('test' + str (i) + '.png') 18 | time.sleep (1) 19 | i = i + 1 20 | 21 | 22 | def main (): 23 | screen_recorder.enable_dev_log () 24 | screen_recorder.set_log_file ('recorder.log') 25 | 26 | params = screen_recorder.RecorderParams () 27 | # params.pid = 0 # use it to set process Id to capture 28 | # params.desktop_num = 0 # use it to set desktop num 29 | 30 | screen_recorder.init_resources (params) 31 | 32 | t = ScreenShoter () 33 | t.start () 34 | screen_recorder.start_video_recording ('video.mp4', 30, 8000000, True) 35 | time.sleep (20) 36 | screen_recorder.stop_video_recording () 37 | t.should_stop = True 38 | t.join () 39 | 40 | screen_recorder.free_resources () 41 | 42 | if __name__ == "__main__": 43 | main () 44 | -------------------------------------------------------------------------------- /python/screen_recorder_sdk/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andrey1994/screen_recorder_sdk/31417c8af136a7b8b44702e69fa0bb6ebb5c2b13/python/screen_recorder_sdk/__init__.py -------------------------------------------------------------------------------- /python/screen_recorder_sdk/exit_codes.py: -------------------------------------------------------------------------------- 1 | import enum 2 | 3 | 4 | class RecorderExitCodes (enum.Enum): 5 | """Enum to store all possible exit codes""" 6 | 7 | STATUS_OK = 0 8 | NO_SUCH_PROCESS_ERROR = 100 9 | RECORDING_ALREADY_RUN_ERROR = 101 10 | RECORDING_THREAD_ERROR = 102 11 | RECORDING_THREAD_IS_NOT_RUNNING_ERROR = 103 12 | INVALID_ARGUMENTS_ERROR = 104 13 | SESSION_NOT_CREATED_ERROR = 105 14 | PREPARE_DESK_DUPL_ERROR = 106 15 | CREATE_TEXTURE_ERROR = 107 16 | DDA_CAPTURE_ERROR = 108 17 | FIND_WINDOW_ERROR = 109 18 | DDA_LOST_ACCESS_ERROR = 110 19 | DDA_TIMEOUT_ERROR = 111 20 | SYNC_TIMEOUT_ERROR = 112 21 | GENERAL_ERROR = 113 22 | -------------------------------------------------------------------------------- /python/screen_recorder_sdk/lib/ScreenRecorder.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andrey1994/screen_recorder_sdk/31417c8af136a7b8b44702e69fa0bb6ebb5c2b13/python/screen_recorder_sdk/lib/ScreenRecorder.dll -------------------------------------------------------------------------------- /python/screen_recorder_sdk/screen_recorder.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import ctypes 4 | import numpy 5 | import enum 6 | import platform 7 | import struct 8 | from numpy.ctypeslib import ndpointer 9 | import pkg_resources 10 | from PIL import Image 11 | from screen_recorder_sdk.exit_codes import RecorderExitCodes 12 | 13 | 14 | class RecorderParams (object): 15 | """ inputs parameters for init_resources method 16 | 17 | :param desktop_num: desktop num, counting from 0 18 | :type desktop_num: int 19 | :param pid: pid of process to capture 20 | :type pid: int 21 | """ 22 | def __init__ (self, desktop_num = 0, pid = 0) -> None: 23 | self.pid = pid 24 | self.desktop_num = desktop_num 25 | 26 | def to_json (self) -> None : 27 | return json.dumps (self, default = lambda o: o.__dict__, 28 | sort_keys = True, indent = 4) 29 | 30 | 31 | class RecorderError (Exception): 32 | """This exception is raised if non-zero exit code is returned from C code 33 | 34 | :param message: exception message 35 | :type message: str 36 | :param exit_code: exit code from low level API 37 | :type exit_code: int 38 | """ 39 | def __init__ (self, message, exit_code): 40 | detailed_message = '%s:%d %s' % (RecorderExitCodes (exit_code).name, exit_code, message) 41 | super (RecorderError, self).__init__ (detailed_message) 42 | self.exit_code = exit_code 43 | 44 | 45 | class ScreenRecorderDLL (object): 46 | 47 | __instance = None 48 | 49 | @classmethod 50 | def get_instance (cls): 51 | if cls.__instance is None: 52 | if platform.system () != 'Windows': 53 | raise Exception ("For now only Windows is supported, detected platform is %s" % platform.system ()) 54 | if struct.calcsize ("P") * 8 != 64: 55 | raise Exception ("You need 64-bit python to use this library") 56 | cls.__instance = cls () 57 | return cls.__instance 58 | 59 | def __init__ (self): 60 | 61 | self.lib = ctypes.cdll.LoadLibrary (pkg_resources.resource_filename (__name__, os.path.join ('lib', 'ScreenRecorder.dll'))) 62 | 63 | self.InitResources = self.lib.InitResources 64 | self.InitResources.restype = ctypes.c_int 65 | self.InitResources.argtypes = [ 66 | ctypes.c_char_p 67 | ] 68 | 69 | self.GetScreenShot = self.lib.GetScreenShot 70 | self.GetScreenShot.restype = ctypes.c_int 71 | self.GetScreenShot.argtypes = [ 72 | ctypes.c_uint, 73 | ndpointer (ctypes.c_ubyte), 74 | ndpointer (ctypes.c_int64), 75 | ndpointer (ctypes.c_int64) 76 | ] 77 | 78 | self.FreeResources = self.lib.FreeResources 79 | self.FreeResources.restype = ctypes.c_int 80 | self.FreeResources.argtypes = [] 81 | 82 | self.SetLogLevel = self.lib.SetLogLevel 83 | self.SetLogLevel.restype = ctypes.c_int 84 | self.SetLogLevel.argtypes = [ 85 | ctypes.c_int64 86 | ] 87 | 88 | self.SetLogFile = self.lib.SetLogFile 89 | self.SetLogFile.restype = ctypes.c_int 90 | self.SetLogFile.argtypes = [ 91 | ctypes.c_char_p 92 | ] 93 | 94 | self.StartVideoRecording = self.lib.StartVideoRecording 95 | self.StartVideoRecording.restype = ctypes.c_int 96 | self.StartVideoRecording.argtypes = [ 97 | ctypes.c_char_p, 98 | ctypes.c_int, 99 | ctypes.c_int, 100 | ctypes.c_int 101 | ] 102 | 103 | self.StopVideoRecording = self.lib.StopVideoRecording 104 | self.StopVideoRecording.restype = ctypes.c_int 105 | self.StopVideoRecording.argtypes = [] 106 | 107 | self.GetPID = self.lib.GetPID 108 | self.GetPID.restype = ctypes.c_int 109 | self.GetPID.argtypes = [ 110 | ndpointer (ctypes.c_int64) 111 | ] 112 | 113 | 114 | def init_resources (params): 115 | """ Init resources for recording 116 | 117 | :param params: params for recording 118 | :type params: RecorderParams 119 | :raises RecorderError: if non zero exit code returned from low level API 120 | """ 121 | try: 122 | input_json = params.to_json ().encode () 123 | except: 124 | input_json = params.to_json () 125 | 126 | res = ScreenRecorderDLL.get_instance ().InitResources (input_json) 127 | if res != RecorderExitCodes.STATUS_OK.value: 128 | raise RecorderError ('unable to init resources', res) 129 | 130 | def get_screenshot (max_attempts = 1): 131 | """ Get Screenshot 132 | 133 | :param max_attempts: max attempts to capture frame buffer 134 | :type max_attempts: int 135 | :return: Pillow Image 136 | :rtype: Pillow Image 137 | :raises RecorderError: if non zero exit code returned from low level API 138 | """ 139 | max_width = 4096 140 | max_height = 4096 141 | max_pixels = max_width * max_height 142 | frame_buffer = numpy.zeros (max_pixels * 4).astype (numpy.uint8) 143 | width = numpy.zeros (1).astype (numpy.int64) 144 | height = numpy.zeros (1).astype (numpy.int64) 145 | 146 | res = ScreenRecorderDLL.get_instance().GetScreenShot (max_attempts, frame_buffer, width, height) 147 | if res != RecorderExitCodes.STATUS_OK.value: 148 | raise RecorderError ('unable to capture FrameBuffer', res) 149 | 150 | return ScreenShotConvertor (frame_buffer, width, height).get_image () 151 | 152 | def get_pid (): 153 | """ Get PID 154 | 155 | :rtype: int 156 | :return: PID 157 | :raises RecorderError: if non zero exit code returned from low level API 158 | """ 159 | pid = numpy.zeros (1).astype (numpy.int64) 160 | res = ScreenRecorderDLL.get_instance ().GetPID (pid) 161 | if res != RecorderExitCodes.STATUS_OK.value: 162 | raise RecorderError ('unable to get pid', res) 163 | return pid[0] 164 | 165 | def free_resources (): 166 | """ Free Resources 167 | 168 | :raises RecorderError: if non zero exit code returned from low level API 169 | """ 170 | res = ScreenRecorderDLL.get_instance ().FreeResources () 171 | if res != RecorderExitCodes.STATUS_OK.value: 172 | raise RecorderError ('unable to free capture resources', res) 173 | 174 | def enable_log (): 175 | """ Enable Logger 176 | 177 | :raises RecorderError: if non zero exit code returned from low level API 178 | """ 179 | res = ScreenRecorderDLL.get_instance ().SetLogLevel (1) 180 | if res != RecorderExitCodes.STATUS_OK.value: 181 | raise RecorderError ('unable to enable capture log', res) 182 | 183 | def enable_dev_log (): 184 | """ Enable Dev Logger 185 | 186 | :raises RecorderError: if non zero exit code returned from low level API 187 | """ 188 | res = ScreenRecorderDLL.get_instance ().SetLogLevel (0) 189 | if res != RecorderExitCodes.STATUS_OK.value: 190 | raise RecorderError ('unable to enable capture log', res) 191 | 192 | def disable_log (): 193 | """ Disable Logger 194 | 195 | :raises RecorderError: if non zero exit code returned from low level API 196 | """ 197 | res = ScreenRecorderDLL.get_instance ().SetLogLevel (6) 198 | if res != RecorderExitCodes.STATUS_OK.value: 199 | raise RecorderError ('unable to disable capture log', res) 200 | 201 | def start_video_recording (filename, frame_rate = 30, bit_rate = 8000000, use_hw_transfowrms = True): 202 | """ Start Video Recording 203 | 204 | :param filename: filename to store video 205 | :type filename: str 206 | :param frame_rate: FPS 207 | :type frame_rate: int 208 | :param bit_rate: bit rate, set higher values for better quality 209 | :type bit_rate: int 210 | :param use_hw_transforms: if you have good GPU set this flag to True for better perf, if you see errors try to set it to false 211 | :type use_hw_transfowrms: bool 212 | :raises RecorderError: if non zero exit code returned from low level API 213 | """ 214 | 215 | res = ScreenRecorderDLL.get_instance ().StartVideoRecording (filename.encode ('utf-8'), 216 | frame_rate, bit_rate, int (use_hw_transfowrms == True)) 217 | if res != RecorderExitCodes.STATUS_OK.value: 218 | raise RecorderError ('unable to start recording video', res) 219 | 220 | def stop_video_recording (): 221 | """ Stop video recording 222 | 223 | :raises RecorderError: if non zero exit code returned from low level API 224 | """ 225 | res = ScreenRecorderDLL.get_instance ().StopVideoRecording () 226 | if res != RecorderExitCodes.STATUS_OK.value: 227 | raise RecorderError ('unable to stop recording video', res) 228 | 229 | def set_log_file (log_file): 230 | """redirect logger from stderr to file, can be called any time 231 | :param log_file: log file name 232 | :type log_file: str 233 | :raises RecorderError: if non zero exit code returned from low level API 234 | """ 235 | try: 236 | file = log_file.encode () 237 | except: 238 | file = log_file 239 | res = ScreenRecorderDLL.get_instance ().SetLogFile (file) 240 | if res != RecorderExitCodes.STATUS_OK.value: 241 | raise RecorderError ('unable to redirect logs to a file', res) 242 | 243 | 244 | class ScreenShotConvertor (object): 245 | 246 | def __init__ (self, frame_buffer, width, height): 247 | self.frame_buffer = frame_buffer 248 | self.width = int (width[0]) 249 | self.height = int (height[0]) 250 | 251 | def get_bgr_array (self): 252 | reshaped = self.frame_buffer[0:self.width*self.height*4].reshape (self.height, self.width*4) 253 | reshaped = reshaped[:,:self.width*4] 254 | reshaped = reshaped.reshape (self.height, self.width, 4) 255 | return reshaped[:,:,:-1] 256 | 257 | def get_rgb_array (self): 258 | return self.get_bgr_array ()[:,:,::-1] 259 | 260 | def get_image (self): 261 | rgb = self.get_rgb_array () 262 | return Image.fromarray (rgb) 263 | -------------------------------------------------------------------------------- /python/setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | from setuptools import setup, find_packages 3 | 4 | this_directory = os.path.abspath (os.path.dirname (__file__)) 5 | with open (os.path.join (this_directory, 'README.md'), encoding = 'utf-8') as f: 6 | long_description = f.read () 7 | 8 | setup ( 9 | name = 'screen_recorder_sdk', 10 | version = os.environ.get ('SCREEN_RECORDER_SDK_VERSION', '0.0.1'), 11 | description = 'Library to take screenshots and record video from desktop', 12 | long_description = long_description, 13 | long_description_content_type = 'text/markdown', 14 | url = 'https://github.com/Andrey1994/screen_recorder_sdk', 15 | author = 'Andrey Parfenov', 16 | author_email = 'a1994ndrey@gmail.com', 17 | packages = find_packages (), 18 | classifiers = [ 19 | 'Development Status :: 2 - Pre-Alpha', 20 | 'Topic :: Utilities' 21 | ], 22 | install_requires = [ 23 | 'numpy', 'Pillow' 24 | ], 25 | package_data = { 26 | 'screen_recorder_sdk': [os.path.join ('lib', 'ScreenRecorder.dll')] 27 | }, 28 | zip_safe = True, 29 | python_requires = '>=3' 30 | ) 31 | -------------------------------------------------------------------------------- /src/ScreenRecorder/MFEncoder.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "MFEncoder.h" 4 | #include "Recorder.h" 5 | 6 | #pragma comment(lib, "mfreadwrite") 7 | #pragma comment(lib, "mfplat") 8 | #pragma comment(lib, "mfuuid") 9 | #pragma comment(lib, "dxguid.lib") 10 | 11 | 12 | MFEncoder::MFEncoder (int bitRate, int fps, const char *fileName, GUID videoInputFormat, 13 | int videoWidth, int videoHeight) 14 | { 15 | this->pWriter = NULL; 16 | this->firstTime = 0; 17 | this->prevTime = 0; 18 | this->streamIndex = 0; 19 | this->bitRate = bitRate; 20 | this->fps = fps; 21 | this->fileName = fileName; 22 | this->videoInputFormat = videoInputFormat; 23 | this->videoHeight = videoHeight; 24 | this->videoWidth = videoWidth; 25 | } 26 | 27 | MFEncoder::~MFEncoder () 28 | { 29 | if (pWriter) 30 | { 31 | HRESULT hr = pWriter->Finalize (); 32 | pWriter->Release (); 33 | pWriter = NULL; 34 | } 35 | MFShutdown (); 36 | } 37 | 38 | HRESULT MFEncoder::InitializeSinkWriter ( 39 | IMFDXGIDeviceManager *deviceManager, bool useHardwareTransform) 40 | { 41 | IMFMediaType *pMediaTypeOut = NULL; 42 | IMFMediaType *pMediaTypeIn = NULL; 43 | IMFAttributes *pAttributes = NULL; 44 | 45 | HRESULT hr = MFStartup (MF_VERSION); 46 | if (useHardwareTransform) 47 | { 48 | const UINT32 cElements = 2; 49 | if (SUCCEEDED (hr)) 50 | { 51 | Recorder::recordLogger->trace ("Before MFCreateAttributes"); 52 | hr = MFCreateAttributes (&pAttributes, cElements); 53 | } 54 | if (SUCCEEDED (hr)) 55 | { 56 | Recorder::recordLogger->trace ("Before set d3d manager"); 57 | hr = pAttributes->SetUnknown (MF_SINK_WRITER_D3D_MANAGER, deviceManager); 58 | } 59 | if (SUCCEEDED (hr)) 60 | { 61 | Recorder::recordLogger->trace ("Before enable hw transforms"); 62 | hr = pAttributes->SetUINT32 (MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, 1); 63 | } 64 | } 65 | else 66 | { 67 | const UINT32 cElements = 1; 68 | if (SUCCEEDED (hr)) 69 | { 70 | Recorder::recordLogger->trace ("Before MFCreateAttributes"); 71 | hr = MFCreateAttributes (&pAttributes, cElements); 72 | } 73 | if (SUCCEEDED (hr)) 74 | { 75 | Recorder::recordLogger->trace ("Before set d3d manager"); 76 | hr = pAttributes->SetUnknown (MF_SINK_WRITER_D3D_MANAGER, deviceManager); 77 | } 78 | } 79 | 80 | if (SUCCEEDED (hr)) 81 | { 82 | Recorder::recordLogger->trace ("Before MFCreateSinkWriterFromURL"); 83 | std::wstring stemp = std::wstring (fileName.begin (), fileName.end ()); 84 | LPCWSTR sw = stemp.c_str (); 85 | hr = MFCreateSinkWriterFromURL (sw, NULL, pAttributes, &pWriter); 86 | } 87 | // Set the output media type. 88 | if (SUCCEEDED (hr)) 89 | { 90 | Recorder::recordLogger->trace ("Before MFCreateMediaType"); 91 | hr = MFCreateMediaType (&pMediaTypeOut); 92 | } 93 | if (SUCCEEDED (hr)) 94 | { 95 | Recorder::recordLogger->trace ("Before SetGUID MF_MT_MAJOR_TYPE"); 96 | hr = pMediaTypeOut->SetGUID (MF_MT_MAJOR_TYPE, MFMediaType_Video); 97 | } 98 | if (SUCCEEDED (hr)) 99 | { 100 | Recorder::recordLogger->trace ("Before SetGUID MF_MT_SUBTYPE"); 101 | hr = pMediaTypeOut->SetGUID (MF_MT_SUBTYPE, videoEncodingFormat); 102 | } 103 | if (SUCCEEDED (hr)) 104 | { 105 | Recorder::recordLogger->trace ("Before SetUINT32 MF_MT_AVG_BITRATE"); 106 | hr = pMediaTypeOut->SetUINT32 (MF_MT_AVG_BITRATE, bitRate); 107 | } 108 | if (SUCCEEDED (hr)) 109 | { 110 | Recorder::recordLogger->trace ("Before SetUINT32 MF_MT_INTERLACE_MODE"); 111 | hr = pMediaTypeOut->SetUINT32 (MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive); 112 | } 113 | if (SUCCEEDED (hr)) 114 | { 115 | Recorder::recordLogger->trace ("Before MFSetAttributeSize MF_MT_FRAME_SIZE"); 116 | hr = MFSetAttributeSize (pMediaTypeOut, MF_MT_FRAME_SIZE, videoWidth, videoHeight); 117 | } 118 | if (SUCCEEDED (hr)) 119 | { 120 | Recorder::recordLogger->trace ("Before MFSetAttributeRatio MF_MT_FRAME_RATE"); 121 | hr = MFSetAttributeRatio (pMediaTypeOut, MF_MT_FRAME_RATE, fps, 1); 122 | } 123 | if (SUCCEEDED (hr)) 124 | { 125 | Recorder::recordLogger->trace ("Before MFSetAttributeRatio MF_MT_FRAME_RATE_RANGE_MAX"); 126 | hr = MFSetAttributeRatio (pMediaTypeOut, MF_MT_FRAME_RATE_RANGE_MAX, 200, 1); 127 | } 128 | if (SUCCEEDED (hr)) 129 | { 130 | Recorder::recordLogger->trace ("Before MFSetAttributeRatio MF_MT_FRAME_RATE_RANGE_MIN"); 131 | hr = MFSetAttributeRatio (pMediaTypeOut, MF_MT_FRAME_RATE_RANGE_MIN, 1, 1); 132 | } 133 | if (SUCCEEDED (hr)) 134 | { 135 | Recorder::recordLogger->trace ("Before MFSetAttributeRatio MF_MT_PIXEL_ASPECT_RATIO"); 136 | hr = MFSetAttributeRatio (pMediaTypeOut, MF_MT_PIXEL_ASPECT_RATIO, 1, 1); 137 | } 138 | if (SUCCEEDED (hr)) 139 | { 140 | Recorder::recordLogger->trace ("Before AddStream"); 141 | hr = pWriter->AddStream (pMediaTypeOut, &streamIndex); 142 | } 143 | 144 | // Set the input media type. 145 | if (SUCCEEDED (hr)) 146 | { 147 | Recorder::recordLogger->trace ("Before MFCreateMediaType"); 148 | hr = MFCreateMediaType (&pMediaTypeIn); 149 | } 150 | if (SUCCEEDED (hr)) 151 | { 152 | Recorder::recordLogger->trace ("Before SetGUID MF_MT_MAJOR_TYPE"); 153 | hr = pMediaTypeIn->SetGUID (MF_MT_MAJOR_TYPE, MFMediaType_Video); 154 | } 155 | if (SUCCEEDED (hr)) 156 | { 157 | Recorder::recordLogger->trace ("Before SetGUID MF_MT_SUBTYPE"); 158 | hr = pMediaTypeIn->SetGUID (MF_MT_SUBTYPE, videoInputFormat); 159 | } 160 | if (SUCCEEDED (hr)) 161 | { 162 | Recorder::recordLogger->trace ("Before SetUINT32 MF_MT_INTERLACE_MODE"); 163 | hr = pMediaTypeIn->SetUINT32 (MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive); 164 | } 165 | if (SUCCEEDED (hr)) 166 | { 167 | Recorder::recordLogger->trace ("Before SetUINT32 MF_SA_D3D11_AWARE"); 168 | hr = pMediaTypeIn->SetUINT32 (MF_SA_D3D11_AWARE, 1); 169 | } 170 | if (SUCCEEDED (hr)) 171 | { 172 | Recorder::recordLogger->trace ("Before MFSetAttributeSize MF_MT_FRAME_SIZE"); 173 | hr = MFSetAttributeSize (pMediaTypeIn, MF_MT_FRAME_SIZE, videoWidth, videoHeight); 174 | } 175 | if (SUCCEEDED (hr)) 176 | { 177 | Recorder::recordLogger->trace ("Before MFSetAttributeRatio MF_MT_FRAME_RATE"); 178 | hr = MFSetAttributeRatio (pMediaTypeIn, MF_MT_FRAME_RATE, fps, 1); 179 | } 180 | if (SUCCEEDED (hr)) 181 | { 182 | Recorder::recordLogger->trace ("Before MFSetAttributeRatio MF_MT_PIXEL_ASPECT_RATIO"); 183 | hr = MFSetAttributeRatio (pMediaTypeIn, MF_MT_PIXEL_ASPECT_RATIO, 1, 1); 184 | } 185 | if (SUCCEEDED (hr)) 186 | { 187 | Recorder::recordLogger->trace ("Before SetInputMediaType streamIndex"); 188 | hr = pWriter->SetInputMediaType (streamIndex, pMediaTypeIn, NULL); 189 | } 190 | if (SUCCEEDED (hr)) 191 | { 192 | Recorder::recordLogger->trace ("Before BeginWriting"); 193 | hr = pWriter->BeginWriting (); 194 | } 195 | 196 | if (pMediaTypeOut) 197 | { 198 | pMediaTypeOut->Release (); 199 | pMediaTypeOut = NULL; 200 | } 201 | if (pMediaTypeIn) 202 | { 203 | pMediaTypeIn->Release (); 204 | pMediaTypeIn = NULL; 205 | } 206 | 207 | return hr; 208 | } 209 | 210 | HRESULT MFEncoder::WriteFrame (IUnknown *texture) 211 | { 212 | FILETIME ft; 213 | GetSystemTimePreciseAsFileTime (&ft); 214 | LONGLONG currTime = ((int64_t)ft.dwHighDateTime << 32L) | (int64_t)ft.dwLowDateTime; 215 | // skip first frame to measure frameduration properly 216 | if ((!firstTime) && (!prevTime)) 217 | { 218 | firstTime = currTime; 219 | prevTime = currTime; 220 | return S_OK; 221 | } 222 | 223 | IMFSample *pSample = NULL; 224 | IMFMediaBuffer *pBuffer = NULL; 225 | 226 | HRESULT hr = MFCreateDXGISurfaceBuffer (IID_ID3D11Texture2D, texture, 0, FALSE, &pBuffer); 227 | if (SUCCEEDED (hr)) 228 | hr = pBuffer->SetCurrentLength (4 * videoWidth * videoHeight); 229 | // Create a media sample and add the buffer to the sample. 230 | if (SUCCEEDED (hr)) 231 | hr = MFCreateSample (&pSample); 232 | if (SUCCEEDED (hr)) 233 | hr = pSample->AddBuffer (pBuffer); 234 | // Set the time stamp and the duration. 235 | if (SUCCEEDED (hr)) 236 | hr = pSample->SetSampleTime (prevTime - firstTime); 237 | if (SUCCEEDED (hr)) 238 | hr = pSample->SetSampleDuration (currTime - prevTime); 239 | // Send the sample to the Sink Writer. 240 | if (SUCCEEDED (hr)) 241 | hr = pWriter->WriteSample (streamIndex, pSample); 242 | if (SUCCEEDED (hr)) 243 | prevTime = currTime; 244 | 245 | if (pSample) 246 | { 247 | pSample->Release (); 248 | pSample = NULL; 249 | } 250 | if (pBuffer) 251 | { 252 | pBuffer->Release (); 253 | pBuffer = NULL; 254 | } 255 | 256 | return hr; 257 | } 258 | -------------------------------------------------------------------------------- /src/ScreenRecorder/Recorder.cpp: -------------------------------------------------------------------------------- 1 | #include "Recorder.h" 2 | #include "ScreenRecorder.h" 3 | 4 | #include "logger/sinks/null_sink.h" 5 | 6 | #define LOGGER_NAME "recordLogger" 7 | 8 | std::shared_ptr Recorder::recordLogger = spdlog::stderr_logger_mt (LOGGER_NAME); 9 | 10 | int Recorder::SetLogLevel (int level) 11 | { 12 | int logLevel = level; 13 | if (level > 6) 14 | logLevel = 6; 15 | if (level < 0) 16 | logLevel = 0; 17 | Recorder::recordLogger->set_level (spdlog::level::level_enum (logLevel)); 18 | Recorder::recordLogger->flush_on (spdlog::level::level_enum (logLevel)); 19 | return STATUS_OK; 20 | } 21 | 22 | int Recorder::SetLogFile (char *logFile) 23 | { 24 | try 25 | { 26 | spdlog::level::level_enum level = Recorder::recordLogger->level (); 27 | Recorder::recordLogger = spdlog::create ( 28 | "null_logger"); // to dont set logger to nullptr and avoid race condition 29 | spdlog::drop (LOGGER_NAME); 30 | Recorder::recordLogger = spdlog::basic_logger_mt (LOGGER_NAME, logFile); 31 | Recorder::recordLogger->set_level (level); 32 | Recorder::recordLogger->flush_on (level); 33 | spdlog::drop ("null_logger"); 34 | } 35 | catch (...) 36 | { 37 | return GENERAL_ERROR; 38 | } 39 | return STATUS_OK; 40 | } 41 | 42 | Recorder::Recorder (struct RecorderParams recorderParams) : params (recorderParams) 43 | { 44 | } 45 | -------------------------------------------------------------------------------- /src/ScreenRecorder/ScreenRecorder.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Recorder.h" 4 | #include "RecorderDDA.h" 5 | #include "RecorderParams.h" 6 | #include "ScreenRecorder.h" 7 | 8 | #include "json.hpp" 9 | 10 | using json = nlohmann::json; 11 | 12 | 13 | Recorder *recorder = NULL; 14 | HANDLE mutex = NULL; 15 | 16 | int InitResources (char *paramsString) 17 | { 18 | if (!mutex) 19 | { 20 | mutex = CreateMutex (NULL, FALSE, NULL); 21 | if (!mutex) 22 | { 23 | return GENERAL_ERROR; 24 | } 25 | } 26 | if (recorder) 27 | { 28 | delete recorder; 29 | recorder = NULL; 30 | } 31 | 32 | struct RecorderParams params; 33 | try 34 | { 35 | json config = json::parse (std::string (paramsString)); 36 | params.pid = config["pid"]; 37 | params.desktopNum = config["desktop_num"]; 38 | } 39 | catch (json::exception &e) 40 | { 41 | return JSON_ERROR; 42 | } 43 | 44 | recorder = new RecorderDDA (params); 45 | WaitForSingleObject (mutex, INFINITE); 46 | int res = recorder->InitResources (); 47 | ReleaseMutex (mutex); 48 | if (res != STATUS_OK) 49 | { 50 | delete recorder; 51 | recorder = NULL; 52 | CloseHandle (mutex); 53 | mutex = NULL; 54 | return res; 55 | } 56 | 57 | return STATUS_OK; 58 | } 59 | 60 | int GetScreenShot (unsigned int maxAttempts, unsigned char *frameBuffer, int *width, int *height) 61 | { 62 | if (!recorder) 63 | return SESSION_NOT_CREATED_ERROR; 64 | 65 | WaitForSingleObject (mutex, INFINITE); 66 | int res = recorder->GetScreenShot (maxAttempts, frameBuffer, width, height); 67 | ReleaseMutex (mutex); 68 | return res; 69 | } 70 | 71 | int FreeResources () 72 | { 73 | if (recorder) 74 | { 75 | if (mutex) 76 | { 77 | WaitForSingleObject (mutex, INFINITE); 78 | delete recorder; 79 | recorder = NULL; 80 | ReleaseMutex (mutex); 81 | CloseHandle (mutex); 82 | mutex = NULL; 83 | } 84 | else 85 | { 86 | return GENERAL_ERROR; 87 | } 88 | } 89 | 90 | return STATUS_OK; 91 | } 92 | 93 | int SetLogLevel (int level) 94 | { 95 | return Recorder::SetLogLevel (level); 96 | } 97 | 98 | int SetLogFile (char *logFile) 99 | { 100 | return Recorder::SetLogFile (logFile); 101 | } 102 | 103 | int StartVideoRecording ( 104 | const char *outputFileName, int frameRate, int bitRate, int useHardwareTransform) 105 | { 106 | if (!recorder) 107 | return SESSION_NOT_CREATED_ERROR; 108 | 109 | WaitForSingleObject (mutex, INFINITE); 110 | bool useTransforms = (useHardwareTransform != 0); 111 | int res = recorder->StartVideoRecording (outputFileName, frameRate, bitRate, useTransforms); 112 | ReleaseMutex (mutex); 113 | return res; 114 | } 115 | 116 | int StopVideoRecording () 117 | { 118 | if (!recorder) 119 | return SESSION_NOT_CREATED_ERROR; 120 | 121 | WaitForSingleObject (mutex, INFINITE); 122 | int res = recorder->StopVideoRecording (); 123 | ReleaseMutex (mutex); 124 | return res; 125 | } 126 | 127 | int GetPID (int *pid) 128 | { 129 | if (!recorder) 130 | return SESSION_NOT_CREATED_ERROR; 131 | 132 | *pid = recorder->GetPID (); 133 | return STATUS_OK; 134 | } 135 | -------------------------------------------------------------------------------- /src/ScreenRecorder/inc/MFEncoder.h: -------------------------------------------------------------------------------- 1 | #ifndef MFENCODER 2 | #define MFENCODER 3 | 4 | // clang-format off 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | // clang-format on 12 | 13 | 14 | class MFEncoder 15 | { 16 | 17 | public: 18 | MFEncoder (int bitRate, int fps, const char *fileName, GUID videoInputFormat, int videoWidth, 19 | int videoHeight); 20 | ~MFEncoder (); 21 | 22 | HRESULT InitializeSinkWriter (IMFDXGIDeviceManager *deviceManager, bool useHardwareTransform); 23 | HRESULT WriteFrame (IUnknown *punkTexture); 24 | 25 | private: 26 | int bitRate; 27 | int fps; 28 | std::string fileName; 29 | GUID videoInputFormat; 30 | int videoWidth; 31 | int videoHeight; 32 | DWORD streamIndex; 33 | IMFSinkWriter *pWriter; 34 | LONGLONG firstTime; 35 | LONGLONG prevTime; 36 | 37 | const GUID videoEncodingFormat = MFVideoFormat_H264; 38 | }; 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/ScreenRecorder/inc/Recorder.h: -------------------------------------------------------------------------------- 1 | #ifndef RECORDER 2 | #define RECORDER 3 | 4 | #include "RecorderParams.h" 5 | 6 | #include "logger\spdlog.h" 7 | 8 | class Recorder 9 | { 10 | public: 11 | static std::shared_ptr recordLogger; 12 | static int SetLogLevel (int level); 13 | static int SetLogFile (char *logFile); 14 | 15 | Recorder (struct RecorderParams recorderParams); 16 | virtual ~Recorder () 17 | { 18 | } 19 | 20 | virtual int InitResources () = 0; 21 | virtual int GetScreenShot ( 22 | unsigned int maxAttempts, unsigned char *frameBuffer, int *width, int *height) = 0; 23 | 24 | virtual int StartVideoRecording ( 25 | const char *outputFileName, int frameRate, int bitRate, bool useHardwareTransform) = 0; 26 | virtual int StopVideoRecording () = 0; 27 | 28 | int GetPID () const 29 | { 30 | return params.pid; 31 | } 32 | 33 | protected: 34 | struct RecorderParams params; 35 | }; 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/ScreenRecorder/inc/RecorderDDA.h: -------------------------------------------------------------------------------- 1 | #ifndef RECORDERDDA 2 | #define RECORDERDDA 3 | 4 | // clang-format off 5 | #include 6 | #include 7 | #include 8 | 9 | #include "ScreenRecorder.h" 10 | #include "MFEncoder.h" 11 | #include "Recorder.h" 12 | // clang-format on 13 | 14 | #define MAX_DISPLAYS 6 15 | 16 | 17 | class RecorderDDA : public Recorder 18 | { 19 | public: 20 | RecorderDDA (struct RecorderParams recoderParams); 21 | ~RecorderDDA (); 22 | 23 | int InitResources (); 24 | int GetScreenShot ( 25 | unsigned int maxAttempts, unsigned char *frameBuffer, int *width, int *height); 26 | int StartVideoRecording ( 27 | const char *outputFileName, int frameRate, int bitRate, bool useHardwareTransform); 28 | int StopVideoRecording (); 29 | 30 | private: 31 | int FreeResources (); 32 | int GetScreenShotOfDisplay ( 33 | unsigned int maxAttempts, ID3D11Texture2D **desktopTexture, unsigned int displayNum); 34 | bool CheckWindowOnDisplay (const RECT &display, const RECT &processRect); 35 | int GetProcessWindow (RECT *rc, int *processDisplay); 36 | int GetImageFromTexture (ID3D11Texture2D *destImage, int displayNum, RECT rcWind, 37 | volatile unsigned char *frameBuffer, volatile int *width, volatile int *height); 38 | int PrepareSession (); 39 | bool CheckThreadAlive (); 40 | int ReleaseDDA (); 41 | 42 | ID3D11Device *device; 43 | ID3D11DeviceContext *immediateContext; 44 | IMFDXGIDeviceManager *deviceManager; 45 | DXGI_OUTPUT_DESC outputDescs[MAX_DISPLAYS]; 46 | IDXGIOutputDuplication *deskDupls[MAX_DISPLAYS]; 47 | D3D11_TEXTURE2D_DESC descs[MAX_DISPLAYS]; 48 | MFEncoder *mfenc; 49 | int totalDisplays; 50 | 51 | volatile unsigned int screenMaxAttempts; 52 | volatile unsigned char *imageBuffer; 53 | volatile int *imageWidth; 54 | volatile int *imageBufferWidth; 55 | volatile int *imageHeight; 56 | volatile char videoFileName[1024]; 57 | volatile int frameRate; 58 | volatile int bitRate; 59 | volatile bool useHardwareTransform; 60 | 61 | static DWORD WINAPI CaptureThreadProc (LPVOID lpParameter); 62 | void WorkerThread (); 63 | volatile HANDLE hThread; 64 | HANDLE hEventSessionReady; 65 | HANDLE hEventStart; 66 | 67 | volatile bool threadMustExit; 68 | volatile int threadRes; 69 | volatile bool needScreen; 70 | volatile bool needVideo; 71 | 72 | CONDITION_VARIABLE condVar; 73 | CRITICAL_SECTION critSect; 74 | 75 | const static D3D_DRIVER_TYPE driverTypes[]; 76 | const static D3D_FEATURE_LEVEL featureLevels[]; 77 | }; 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /src/ScreenRecorder/inc/RecorderParams.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | struct RecorderParams 5 | { 6 | int pid; 7 | int desktopNum; 8 | 9 | RecorderParams (int pid, int desktopNum) 10 | { 11 | this->pid = pid; 12 | this->desktopNum = desktopNum; 13 | } 14 | 15 | RecorderParams () 16 | { 17 | this->pid = 0; 18 | this->desktopNum = 0; 19 | } 20 | }; -------------------------------------------------------------------------------- /src/ScreenRecorder/inc/ScreenRecorder.h: -------------------------------------------------------------------------------- 1 | #ifndef SCREENRECORDER 2 | #define SCREENRECORDER 3 | 4 | typedef enum 5 | { 6 | STATUS_OK = 0, 7 | NO_SUCH_PROCESS_ERROR = 100, 8 | RECORDING_ALREADY_RUN_ERROR, 9 | RECORDING_THREAD_ERROR, 10 | RECORDING_THREAD_IS_NOT_RUNNING_ERROR, 11 | INVALID_ARGUMENTS_ERROR, 12 | SESSION_NOT_CREATED_ERROR, 13 | PREPARE_DESK_DUPL_ERROR, 14 | CREATE_TEXTURE_ERROR, 15 | DDA_CAPTURE_ERROR, 16 | FIND_WINDOW_ERROR, 17 | DDA_LOST_ACCESS_ERROR, 18 | DDA_TIMEOUT_ERROR, 19 | SYNC_TIMEOUT_ERROR, 20 | GENERAL_ERROR, 21 | JSON_ERROR 22 | } ExitCodes; 23 | 24 | extern "C" 25 | { 26 | __declspec(dllexport) int __cdecl InitResources (char *paramsString); 27 | __declspec(dllexport) int __cdecl GetScreenShot ( 28 | unsigned int maxAttempts, unsigned char *frameBuffer, int *width, int *height); 29 | __declspec(dllexport) int __cdecl FreeResources (); 30 | __declspec(dllexport) int __cdecl SetLogLevel (int level); 31 | __declspec(dllexport) int __cdecl SetLogFile (char *logFile); 32 | __declspec(dllexport) int __cdecl GetPID (int *pid); 33 | 34 | __declspec(dllexport) int __cdecl StartVideoRecording ( 35 | const char *outputFileName, int frameRate, int bitRate, int useHardwareTransform); 36 | __declspec(dllexport) int __cdecl StopVideoRecording (); 37 | } 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/Utils/HandleD3DWindow.cpp: -------------------------------------------------------------------------------- 1 | #include "HandleD3DWindow.h" 2 | 3 | 4 | HWND GetMainWindow (unsigned long pid) 5 | { 6 | WindowData data; 7 | data.pid = pid; 8 | data.wHandle = 0; 9 | EnumWindows (EnumWindowsCallback, (LPARAM)&data); 10 | return data.wHandle; 11 | } 12 | 13 | BOOL CALLBACK EnumWindowsCallback (HWND handle, LPARAM lparam) 14 | { 15 | WindowData& data = *(WindowData*)lparam; 16 | unsigned long pid = 0; 17 | GetWindowThreadProcessId (handle, &pid); 18 | if (data.pid != pid || !IsMainWindow (handle)) 19 | return TRUE; 20 | data.wHandle = handle; 21 | return FALSE; 22 | } 23 | 24 | BOOL IsMainWindow (HWND handle) 25 | { 26 | return GetWindow (handle, GW_OWNER) == (HWND)0 && IsWindowVisible (handle); 27 | } 28 | -------------------------------------------------------------------------------- /src/Utils/inc/HandleD3DWindow.h: -------------------------------------------------------------------------------- 1 | #ifndef HANDLE_D3D_WINDOW 2 | #define HANDLE_D3D_WINDOW 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | struct WindowData { 10 | unsigned long pid; 11 | HWND wHandle; 12 | }; 13 | 14 | HWND GetMainWindow (unsigned long pid); 15 | BOOL CALLBACK EnumWindowsCallback (HWND handle, LPARAM lparam); 16 | BOOL IsMainWindow (HWND handle); 17 | 18 | #endif --------------------------------------------------------------------------------