├── .gitattributes ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── LICENCE ├── README.md ├── img └── logo.png └── real-app ├── CMakeLists.txt ├── deps ├── curl │ ├── CMakeLists.txt.in │ ├── build.bat │ └── curl-config.cmake ├── expected │ ├── COPYING │ ├── expected-config.cmake │ └── include │ │ └── tl │ │ └── expected.hpp ├── json │ ├── json-config.cmake │ └── nlohmann │ │ └── json.hpp └── spdlog │ ├── LICENSE │ ├── include │ └── spdlog │ │ ├── async.h │ │ ├── async_logger.h │ │ ├── common.h │ │ ├── details │ │ ├── async_logger_impl.h │ │ ├── circular_q.h │ │ ├── console_globals.h │ │ ├── file_helper.h │ │ ├── fmt_helper.h │ │ ├── log_msg.h │ │ ├── logger_impl.h │ │ ├── mpmc_blocking_q.h │ │ ├── null_mutex.h │ │ ├── os.h │ │ ├── pattern_formatter.h │ │ ├── periodic_worker.h │ │ ├── registry.h │ │ └── thread_pool.h │ │ ├── fmt │ │ ├── bundled │ │ │ ├── LICENSE.rst │ │ │ ├── colors.h │ │ │ ├── core.h │ │ │ ├── format-inl.h │ │ │ ├── format.h │ │ │ ├── locale.h │ │ │ ├── ostream.h │ │ │ ├── posix.h │ │ │ ├── printf.h │ │ │ ├── ranges.h │ │ │ └── time.h │ │ ├── fmt.h │ │ └── ostr.h │ │ ├── formatter.h │ │ ├── logger.h │ │ ├── sinks │ │ ├── android_sink.h │ │ ├── ansicolor_sink.h │ │ ├── base_sink.h │ │ ├── basic_file_sink.h │ │ ├── daily_file_sink.h │ │ ├── dist_sink.h │ │ ├── msvc_sink.h │ │ ├── null_sink.h │ │ ├── ostream_sink.h │ │ ├── rotating_file_sink.h │ │ ├── sink.h │ │ ├── stdout_color_sinks.h │ │ ├── stdout_sinks.h │ │ ├── syslog_sink.h │ │ └── wincolor_sink.h │ │ ├── spdlog.h │ │ ├── tweakme.h │ │ └── version.h │ └── spdlog-config.cmake ├── res ├── app.ico ├── real-app.rc └── resource.h ├── run-cmake.bat └── src ├── AutoUpdater.cpp ├── AutoUpdater.h ├── CurlWrapper ├── CurlError.cpp ├── CurlError.h ├── CurlHandle.cpp ├── CurlHandle.h └── Writers │ ├── CurlFileWriter.cpp │ ├── CurlFileWriter.h │ ├── CurlMemoryWriter.cpp │ ├── CurlMemoryWriter.h │ ├── CurlWriter.cpp │ └── CurlWriter.h ├── ExpectedError.h ├── OStreamSink.cpp ├── OStreamSink.h ├── Version.cpp ├── Version.h ├── Windows ├── Console.cpp ├── Console.h ├── Filesystem.cpp ├── Filesystem.h ├── GlobalWindowProcedure.cpp ├── GlobalWindowProcedure.h ├── MessagingWindow.cpp ├── MessagingWindow.h ├── MinimumLatencyAudioClient.cpp ├── MinimumLatencyAudioClient.h ├── TrayIcon.cpp ├── TrayIcon.h ├── WindowsError.cpp └── WindowsError.h └── main.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **REAL version**: [e.g. v0.1.1] 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 1. Go to '...' 15 | 2. Click on '....' 16 | 3. Scroll down to '....' 17 | 4. See error 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | 25 | **Additional context** 26 | Add any other context about the problem here. 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | real-app/build/ 2 | 3 | ## Ignore Visual Studio temporary files, build results, and 4 | ## files generated by popular Visual Studio add-ons. 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 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # DNX 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | 50 | *_i.c 51 | *_p.c 52 | *_i.h 53 | *.ilk 54 | *.meta 55 | *.obj 56 | *.pch 57 | *.pdb 58 | *.pgc 59 | *.pgd 60 | *.rsp 61 | *.sbr 62 | *.tlb 63 | *.tli 64 | *.tlh 65 | *.tmp 66 | *.tmp_proj 67 | *.log 68 | *.vspscc 69 | *.vssscc 70 | .builds 71 | *.pidb 72 | *.svclog 73 | *.scc 74 | 75 | # Chutzpah Test files 76 | _Chutzpah* 77 | 78 | # Visual C++ cache files 79 | ipch/ 80 | *.aps 81 | *.ncb 82 | *.opendb 83 | *.opensdf 84 | *.sdf 85 | *.cachefile 86 | *.VC.db 87 | *.VC.VC.opendb 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | *.sap 94 | 95 | # TFS 2012 Local Workspace 96 | $tf/ 97 | 98 | # Guidance Automation Toolkit 99 | *.gpState 100 | 101 | # ReSharper is a .NET coding add-in 102 | _ReSharper*/ 103 | *.[Rr]e[Ss]harper 104 | *.DotSettings.user 105 | 106 | # JustCode is a .NET coding add-in 107 | .JustCode 108 | 109 | # TeamCity is a build add-in 110 | _TeamCity* 111 | 112 | # DotCover is a Code Coverage Tool 113 | *.dotCover 114 | 115 | # NCrunch 116 | _NCrunch_* 117 | .*crunch*.local.xml 118 | nCrunchTemp_* 119 | 120 | # MightyMoose 121 | *.mm.* 122 | AutoTest.Net/ 123 | 124 | # Web workbench (sass) 125 | .sass-cache/ 126 | 127 | # Installshield output folder 128 | [Ee]xpress/ 129 | 130 | # DocProject is a documentation generator add-in 131 | DocProject/buildhelp/ 132 | DocProject/Help/*.HxT 133 | DocProject/Help/*.HxC 134 | DocProject/Help/*.hhc 135 | DocProject/Help/*.hhk 136 | DocProject/Help/*.hhp 137 | DocProject/Help/Html2 138 | DocProject/Help/html 139 | 140 | # Click-Once directory 141 | publish/ 142 | 143 | # Publish Web Output 144 | *.[Pp]ublish.xml 145 | *.azurePubxml 146 | # TODO: Comment the next line if you want to checkin your web deploy settings 147 | # but database connection strings (with potential passwords) will be unencrypted 148 | #*.pubxml 149 | *.publishproj 150 | 151 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 152 | # checkin your Azure Web App publish settings, but sensitive information contained 153 | # in these scripts will be unencrypted 154 | PublishScripts/ 155 | 156 | # NuGet Packages 157 | *.nupkg 158 | # The packages folder can be ignored because of Package Restore 159 | **/packages/* 160 | # except build/, which is used as an MSBuild target. 161 | !**/packages/build/ 162 | # Uncomment if necessary however generally it will be regenerated when needed 163 | #!**/packages/repositories.config 164 | # NuGet v3's project.json files produces more ignoreable files 165 | *.nuget.props 166 | *.nuget.targets 167 | 168 | # Microsoft Azure Build Output 169 | csx/ 170 | *.build.csdef 171 | 172 | # Microsoft Azure Emulator 173 | ecf/ 174 | rcf/ 175 | 176 | # Windows Store app package directories and files 177 | AppPackages/ 178 | BundleArtifacts/ 179 | Package.StoreAssociation.xml 180 | _pkginfo.txt 181 | 182 | # Visual Studio cache files 183 | # files ending in .cache can be ignored 184 | *.[Cc]ache 185 | # but keep track of directories ending in .cache 186 | !*.[Cc]ache/ 187 | 188 | # Others 189 | ClientBin/ 190 | ~$* 191 | *~ 192 | *.dbmdl 193 | *.dbproj.schemaview 194 | *.jfm 195 | *.pfx 196 | *.publishsettings 197 | node_modules/ 198 | orleans.codegen.cs 199 | 200 | # Since there are multiple workflows, uncomment next line to ignore bower_components 201 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 202 | #bower_components/ 203 | 204 | # RIA/Silverlight projects 205 | Generated_Code/ 206 | 207 | # Backup & report files from converting an old project file 208 | # to a newer Visual Studio version. Backup files are not needed, 209 | # because we have git ;-) 210 | _UpgradeReport_Files/ 211 | Backup*/ 212 | UpgradeLog*.XML 213 | UpgradeLog*.htm 214 | 215 | # SQL Server files 216 | *.mdf 217 | *.ldf 218 | 219 | # Business Intelligence projects 220 | *.rdl.data 221 | *.bim.layout 222 | *.bim_*.settings 223 | 224 | # Microsoft Fakes 225 | FakesAssemblies/ 226 | 227 | # GhostDoc plugin setting file 228 | *.GhostDoc.xml 229 | 230 | # Node.js Tools for Visual Studio 231 | .ntvs_analysis.dat 232 | 233 | # Visual Studio 6 build log 234 | *.plg 235 | 236 | # Visual Studio 6 workspace options file 237 | *.opt 238 | 239 | # Visual Studio LightSwitch build output 240 | **/*.HTMLClient/GeneratedArtifacts 241 | **/*.DesktopClient/GeneratedArtifacts 242 | **/*.DesktopClient/ModelManifest.xml 243 | **/*.Server/GeneratedArtifacts 244 | **/*.Server/ModelManifest.xml 245 | _Pvt_Extensions 246 | 247 | # Paket dependency manager 248 | .paket/paket.exe 249 | paket-files/ 250 | 251 | # FAKE - F# Make 252 | .fake/ 253 | 254 | # JetBrains Rider 255 | .idea/ 256 | *.sln.iml 257 | 258 | # CodeRush 259 | .cr/ 260 | 261 | # Python Tools for Visual Studio (PTVS) 262 | __pycache__/ 263 | *.pyc -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 mini)(ant 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![REAL](img/logo.png) 2 | 3 | --- 4 | 5 | ## Features 6 | 7 | * Audio latency reduction on the default playback device 8 | * Automatic updates 9 | * Starting minimised 10 | 11 | ## Requirements 12 | 13 | * Windows 10 64-bit 14 | * [Microsoft Visual C++ 2017 Redistributable (x64)](https://aka.ms/vs/15/release/VC_redist.x64.exe) 15 | 16 | ## Setup 17 | 18 | 1. Install Windows' in-box HDAudio driver (optional, might improve latency): 19 | 1. Start **Device Manager**. 20 | 2. Under **Sound, video and game controllers**, double click on the device that corresponds to your speakers. 21 | 3. In the next window, go to the **Driver** tab. 22 | 4. Select **Update driver** -> **Browse my computer for driver software** -> **Let me pick from a list of available drivers on my computer**. 23 | 5. Select **High Definition Audio Device** and click **Next**. 24 | 6. If a window titled "Update Driver warning" appears, click **Yes**. 25 | 7. Select **Close**. 26 | 8. If asked to reboot the system, select **Yes** to reboot. 27 | > **Be careful**: the new driver might reset your volume to uncomfortably high levels. 28 | 2. Download the [latest version](https://github.com/miniant-git/REAL/releases/latest) of **REAL**. 29 | 3. Launch `REAL.exe`. The latency reduction is in effect as long as the application is kept running. 30 | 31 | ## Command-Line Options 32 | * `--tray` Launches the application minimised to the system tray 33 | 34 | ## Building 35 | 36 | 1. Make sure **Microsoft Visual Studio 2017** is installed and updated. 37 | 2. Install [CMake 3.12](https://cmake.org/download/) or later and configure your `PATH` environment variable to find `cmake` if necessary. 38 | 3. Clone the repository: 39 | ```bat 40 | git clone https://github.com/miniant-git/REAL.git miniant-real 41 | cd miniant-real 42 | ``` 43 | 4. Configure Visual Studio project with CMake: 44 | ```bat 45 | cd real-app 46 | ./run-cmake.bat 47 | ``` 48 | 5. Open the generated solution: 49 | ```bat 50 | start build/miniant-real.sln 51 | ``` 52 | 6. Right-click on the `real-app` project in the **Solution Explorer** and select **Build**. 53 | * The resulting executable will be placed inside `real-app/build/Debug/` folder. 54 | 55 | ## FAQ 56 | 57 | ### How does this work? 58 | 59 | As described in Mirosoft's [Low Latency Audio FAQ section](https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/low-latency-audio#span-idfaqspanspan-idfaqspanfaq), by default, all applications in Windows 10 use 10ms buffers to render audio. However, if one application requests the usage of small buffers, then the Audio Engine will start transferring audio using that particular buffer size. In that case, all applications that use the same endpoint (device) and mode (either exclusive or shared) will automatically switch to that small buffer size. We make use of this Audio Engine property by starting a rendering stream which requests the minimal buffer size that is supported by the audio driver. 60 | 61 | ### What are the downsides? 62 | 63 | Since the application reduces audio sample buffer size, the buffer runs out faster and needs to be refilled more frequently. This increases the odds of audible audio cracks appearing when the CPU is busy and unable to keep up. 64 | -------------------------------------------------------------------------------- /img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/miniant-git/REAL/077e6fe2e6242f41bf401960562ccfecc6ca6a18/img/logo.png -------------------------------------------------------------------------------- /real-app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | project(miniant-real) 4 | 5 | set(CMAKE_CXX_STANDARD 17) 6 | set(CMAKE_BUILD_TYPE Debug) 7 | 8 | FILE(GLOB_RECURSE REAL_APP_INPUT src/*.cpp src/*.h res/*.rc res/*.h) 9 | add_executable(real-app WIN32 ${REAL_APP_INPUT}) 10 | 11 | target_compile_definitions(real-app PUBLIC _UNICODE UNICODE) 12 | target_compile_options(real-app PRIVATE /JMC) 13 | 14 | set(CMAKE_PREFIX_PATH "deps") 15 | 16 | find_package(curl REQUIRED CONFIG) 17 | target_link_libraries(real-app curl) 18 | 19 | find_package(expected REQUIRED) 20 | target_link_libraries(real-app expected) 21 | 22 | find_package(json REQUIRED) 23 | target_link_libraries(real-app json) 24 | 25 | find_package(spdlog REQUIRED) 26 | target_link_libraries(real-app spdlog) 27 | 28 | source_group(TREE . FILES ${REAL_APP_INPUT}) 29 | -------------------------------------------------------------------------------- /real-app/deps/curl/CMakeLists.txt.in: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | include(ExternalProject) 4 | ExternalProject_Add( 5 | curl-git 6 | PREFIX "." 7 | TMP_DIR "tmp" 8 | STAMP_DIR "stamp" 9 | DOWNLOAD_DIR "git" 10 | SOURCE_DIR "git" 11 | GIT_REPOSITORY https://github.com/curl/curl 12 | GIT_TAG tags/curl-7_62_0 13 | GIT_SHALLOW true 14 | GIT_PROGRESS true 15 | CONFIGURE_COMMAND buildconf.bat 16 | BUILD_COMMAND "${CMAKE_CURRENT_LIST_DIR}/build.bat" 17 | BUILD_IN_SOURCE true 18 | INSTALL_COMMAND "") 19 | -------------------------------------------------------------------------------- /real-app/deps/curl/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | for /f "usebackq tokens=*" %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do ( 4 | set InstallDir=%%i 5 | ) 6 | 7 | call "%InstallDir%\VC\Auxiliary\Build\vcvars64.bat" 8 | cd winbuild && nmake /f Makefile.vc mode=static VC=15 MACHINE=x64 DEBUG=yes 9 | -------------------------------------------------------------------------------- /real-app/deps/curl/curl-config.cmake: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | configure_file(${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt.in curl/CMakeLists.txt) 4 | execute_process( 5 | COMMAND "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" . 6 | WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/curl") 7 | execute_process( 8 | COMMAND "${CMAKE_COMMAND}" --build . 9 | WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/curl") 10 | 11 | set(CURL_BUILD_OUTPUT "${CMAKE_BINARY_DIR}/curl/git/builds/libcurl-vc15-x64-debug-static-ipv6-sspi-winssl") 12 | 13 | add_library(curl STATIC IMPORTED) 14 | set_target_properties(curl PROPERTIES IMPORTED_LOCATION "${CURL_BUILD_OUTPUT}/lib/libcurl_a_debug.lib") 15 | 16 | target_include_directories(curl INTERFACE "${CURL_BUILD_OUTPUT}/include") 17 | target_link_libraries(curl INTERFACE Ws2_32 crypt32 Wldap32 Normaliz) 18 | target_compile_definitions(curl INTERFACE CURL_STATICLIB) 19 | -------------------------------------------------------------------------------- /real-app/deps/expected/COPYING: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /real-app/deps/expected/expected-config.cmake: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | add_library(expected INTERFACE) 4 | target_include_directories(expected INTERFACE "${CMAKE_CURRENT_LIST_DIR}/include") 5 | -------------------------------------------------------------------------------- /real-app/deps/json/json-config.cmake: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | add_library(json INTERFACE) 4 | target_include_directories(json INTERFACE "${CMAKE_CURRENT_LIST_DIR}") 5 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Gabi Melman. 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 13 | all 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 21 | THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/async.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright(c) 2018 Gabi Melman. 4 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 5 | // 6 | 7 | #pragma once 8 | 9 | // 10 | // Async logging using global thread pool 11 | // All loggers created here share same global thread pool. 12 | // Each log message is pushed to a queue along withe a shared pointer to the 13 | // logger. 14 | // If a logger deleted while having pending messages in the queue, it's actual 15 | // destruction will defer 16 | // until all its messages are processed by the thread pool. 17 | // This is because each message in the queue holds a shared_ptr to the 18 | // originating logger. 19 | 20 | #include "spdlog/async_logger.h" 21 | #include "spdlog/details/registry.h" 22 | #include "spdlog/details/thread_pool.h" 23 | 24 | #include 25 | #include 26 | 27 | namespace spdlog { 28 | 29 | namespace details { 30 | static const size_t default_async_q_size = 8192; 31 | } 32 | 33 | // async logger factory - creates async loggers backed with thread pool. 34 | // if a global thread pool doesn't already exist, create it with default queue 35 | // size of 8192 items and single thread. 36 | template 37 | struct async_factory_impl 38 | { 39 | template 40 | static std::shared_ptr create(const std::string &logger_name, SinkArgs &&... args) 41 | { 42 | auto ®istry_inst = details::registry::instance(); 43 | 44 | // create global thread pool if not already exists.. 45 | std::lock_guard tp_lock(registry_inst.tp_mutex()); 46 | auto tp = registry_inst.get_tp(); 47 | if (tp == nullptr) 48 | { 49 | tp = std::make_shared(details::default_async_q_size, 1); 50 | registry_inst.set_tp(tp); 51 | } 52 | 53 | auto sink = std::make_shared(std::forward(args)...); 54 | auto new_logger = std::make_shared(logger_name, std::move(sink), std::move(tp), OverflowPolicy); 55 | registry_inst.register_and_init(new_logger); 56 | return new_logger; 57 | } 58 | }; 59 | 60 | using async_factory = async_factory_impl; 61 | using async_factory_nonblock = async_factory_impl; 62 | 63 | template 64 | inline std::shared_ptr create_async(const std::string &logger_name, SinkArgs &&... sink_args) 65 | { 66 | return async_factory::create(logger_name, std::forward(sink_args)...); 67 | } 68 | 69 | template 70 | inline std::shared_ptr create_async_nb(const std::string &logger_name, SinkArgs &&... sink_args) 71 | { 72 | return async_factory_nonblock::create(logger_name, std::forward(sink_args)...); 73 | } 74 | 75 | // set global thread pool. 76 | inline void init_thread_pool(size_t q_size, size_t thread_count) 77 | { 78 | auto tp = std::make_shared(q_size, thread_count); 79 | details::registry::instance().set_tp(std::move(tp)); 80 | } 81 | 82 | // get the global thread pool. 83 | inline std::shared_ptr thread_pool() 84 | { 85 | return details::registry::instance().get_tp(); 86 | } 87 | } // namespace spdlog 88 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/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 9 | // desktop) 10 | // Uses pre allocated lockfree queue for maximum throughput even under large 11 | // number of threads. 12 | // Creates a single back thread to pop messages from the queue and log them. 13 | // 14 | // Upon each log write the logger: 15 | // 1. Checks if its log level is enough to log the message 16 | // 2. Push a new copy of the message to a queue (or block the caller until 17 | // space is available in the queue) 18 | // 3. will throw spdlog_ex upon log exceptions 19 | // Upon destruction, logs all remaining messages in the queue before 20 | // destructing.. 21 | 22 | #include "spdlog/common.h" 23 | #include "spdlog/logger.h" 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | namespace spdlog { 30 | 31 | // Async overflow policy - block by default. 32 | enum class async_overflow_policy 33 | { 34 | block, // Block until message can be enqueued 35 | overrun_oldest // Discard oldest message in the queue if full when trying to 36 | // add new item. 37 | }; 38 | 39 | namespace details { 40 | class thread_pool; 41 | } 42 | 43 | class async_logger SPDLOG_FINAL : public std::enable_shared_from_this, public logger 44 | { 45 | friend class details::thread_pool; 46 | 47 | public: 48 | template 49 | async_logger(std::string logger_name, const It &begin, const It &end, std::weak_ptr tp, 50 | async_overflow_policy overflow_policy = async_overflow_policy::block); 51 | 52 | async_logger(std::string logger_name, sinks_init_list sinks_list, std::weak_ptr tp, 53 | async_overflow_policy overflow_policy = async_overflow_policy::block); 54 | 55 | async_logger(std::string logger_name, sink_ptr single_sink, std::weak_ptr tp, 56 | async_overflow_policy overflow_policy = async_overflow_policy::block); 57 | 58 | std::shared_ptr clone(std::string new_name) override; 59 | 60 | protected: 61 | void sink_it_(details::log_msg &msg) override; 62 | void flush_() override; 63 | 64 | void backend_log_(details::log_msg &incoming_log_msg); 65 | void backend_flush_(); 66 | 67 | private: 68 | std::weak_ptr thread_pool_; 69 | async_overflow_policy overflow_policy_; 70 | }; 71 | } // namespace spdlog 72 | 73 | #include "details/async_logger_impl.h" 74 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/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 | #include "spdlog/tweakme.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) 20 | #include 21 | #include 22 | #endif 23 | 24 | #include "spdlog/details/null_mutex.h" 25 | 26 | // visual studio upto 2013 does not support noexcept nor constexpr 27 | #if defined(_MSC_VER) && (_MSC_VER < 1900) 28 | #define SPDLOG_NOEXCEPT throw() 29 | #define SPDLOG_CONSTEXPR 30 | #else 31 | #define SPDLOG_NOEXCEPT noexcept 32 | #define SPDLOG_CONSTEXPR constexpr 33 | #endif 34 | 35 | // final keyword support. On by default. See tweakme.h 36 | #if defined(SPDLOG_NO_FINAL) 37 | #define SPDLOG_FINAL 38 | #else 39 | #define SPDLOG_FINAL final 40 | #endif 41 | 42 | #if defined(__GNUC__) || defined(__clang__) 43 | #define SPDLOG_DEPRECATED __attribute__((deprecated)) 44 | #elif defined(_MSC_VER) 45 | #define SPDLOG_DEPRECATED __declspec(deprecated) 46 | #else 47 | #define SPDLOG_DEPRECATED 48 | #endif 49 | 50 | #include "spdlog/fmt/fmt.h" 51 | 52 | namespace spdlog { 53 | 54 | class formatter; 55 | 56 | namespace sinks { 57 | class sink; 58 | } 59 | 60 | using log_clock = std::chrono::system_clock; 61 | using sink_ptr = std::shared_ptr; 62 | using sinks_init_list = std::initializer_list; 63 | using log_err_handler = std::function; 64 | 65 | #if defined(SPDLOG_NO_ATOMIC_LEVELS) 66 | using level_t = details::null_atomic_int; 67 | #else 68 | using level_t = std::atomic; 69 | #endif 70 | 71 | // Log level enum 72 | namespace level { 73 | enum level_enum 74 | { 75 | trace = 0, 76 | debug = 1, 77 | info = 2, 78 | warn = 3, 79 | err = 4, 80 | critical = 5, 81 | off = 6 82 | }; 83 | 84 | #if !defined(SPDLOG_LEVEL_NAMES) 85 | #define SPDLOG_LEVEL_NAMES \ 86 | { \ 87 | "trace", "debug", "info", "warning", "error", "critical", "off" \ 88 | } 89 | #endif 90 | static const char *level_names[] SPDLOG_LEVEL_NAMES; 91 | 92 | static const char *short_level_names[]{"T", "D", "I", "W", "E", "C", "O"}; 93 | 94 | inline const char *to_c_str(spdlog::level::level_enum l) 95 | { 96 | return level_names[l]; 97 | } 98 | 99 | inline const char *to_short_c_str(spdlog::level::level_enum l) 100 | { 101 | return short_level_names[l]; 102 | } 103 | 104 | inline spdlog::level::level_enum from_str(const std::string &name) 105 | { 106 | static std::unordered_map name_to_level = // map string->level 107 | {{level_names[0], level::trace}, // trace 108 | {level_names[1], level::debug}, // debug 109 | {level_names[2], level::info}, // info 110 | {level_names[3], level::warn}, // warn 111 | {level_names[4], level::err}, // err 112 | {level_names[5], level::critical}, // critical 113 | {level_names[6], level::off}}; // off 114 | 115 | auto lvl_it = name_to_level.find(name); 116 | return lvl_it != name_to_level.end() ? lvl_it->second : level::off; 117 | } 118 | 119 | using level_hasher = std::hash; 120 | } // namespace level 121 | 122 | // 123 | // Pattern time - specific time getting to use for pattern_formatter. 124 | // local time by default 125 | // 126 | enum class pattern_time_type 127 | { 128 | local, // log localtime 129 | utc // log utc 130 | }; 131 | 132 | // 133 | // Log exception 134 | // 135 | class spdlog_ex : public std::exception 136 | { 137 | public: 138 | explicit spdlog_ex(const std::string &msg) 139 | : msg_(msg) 140 | { 141 | } 142 | 143 | spdlog_ex(const std::string &msg, int last_errno) 144 | { 145 | fmt::memory_buffer outbuf; 146 | fmt::format_system_error(outbuf, last_errno, msg); 147 | msg_ = fmt::to_string(outbuf); 148 | } 149 | 150 | const char *what() const SPDLOG_NOEXCEPT override 151 | { 152 | return msg_.c_str(); 153 | } 154 | 155 | private: 156 | std::string msg_; 157 | }; 158 | 159 | // 160 | // wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) 161 | // 162 | #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) 163 | using filename_t = std::wstring; 164 | #else 165 | using filename_t = std::string; 166 | #endif 167 | 168 | #define SPDLOG_CATCH_AND_HANDLE \ 169 | catch (const std::exception &ex) \ 170 | { \ 171 | err_handler_(ex.what()); \ 172 | } \ 173 | catch (...) \ 174 | { \ 175 | err_handler_("Unknown exeption in logger"); \ 176 | } 177 | 178 | // 179 | // make_unique support 180 | // 181 | #if __cplusplus >= 201402L // C++14 and beyond 182 | using std::make_unique; 183 | #else 184 | template 185 | std::unique_ptr make_unique(Args &&... args) 186 | { 187 | return std::unique_ptr(new T(std::forward(args)...)); 188 | } 189 | #endif 190 | } // namespace spdlog 191 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/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 | // uses a thread pool to perform the actual logging 10 | 11 | #include "spdlog/details/thread_pool.h" 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | template 18 | inline spdlog::async_logger::async_logger( 19 | std::string logger_name, const It &begin, const It &end, std::weak_ptr tp, async_overflow_policy overflow_policy) 20 | : logger(std::move(logger_name), begin, end) 21 | , thread_pool_(tp) 22 | , overflow_policy_(overflow_policy) 23 | { 24 | } 25 | 26 | inline spdlog::async_logger::async_logger( 27 | std::string logger_name, sinks_init_list sinks_list, std::weak_ptr tp, async_overflow_policy overflow_policy) 28 | : async_logger(std::move(logger_name), sinks_list.begin(), sinks_list.end(), tp, overflow_policy) 29 | { 30 | } 31 | 32 | inline spdlog::async_logger::async_logger( 33 | std::string logger_name, sink_ptr single_sink, std::weak_ptr tp, async_overflow_policy overflow_policy) 34 | : async_logger(std::move(logger_name), {single_sink}, tp, overflow_policy) 35 | { 36 | } 37 | 38 | // send the log message to the thread pool 39 | inline void spdlog::async_logger::sink_it_(details::log_msg &msg) 40 | { 41 | #if defined(SPDLOG_ENABLE_MESSAGE_COUNTER) 42 | incr_msg_counter_(msg); 43 | #endif 44 | if (auto pool_ptr = thread_pool_.lock()) 45 | { 46 | pool_ptr->post_log(shared_from_this(), std::move(msg), overflow_policy_); 47 | } 48 | else 49 | { 50 | throw spdlog_ex("async log: thread pool doesn't exist anymore"); 51 | } 52 | } 53 | 54 | // send flush request to the thread pool 55 | inline void spdlog::async_logger::flush_() 56 | { 57 | if (auto pool_ptr = thread_pool_.lock()) 58 | { 59 | pool_ptr->post_flush(shared_from_this(), overflow_policy_); 60 | } 61 | else 62 | { 63 | throw spdlog_ex("async flush: thread pool doesn't exist anymore"); 64 | } 65 | } 66 | 67 | // 68 | // backend functions - called from the thread pool to do the actual job 69 | // 70 | inline void spdlog::async_logger::backend_log_(details::log_msg &incoming_log_msg) 71 | { 72 | try 73 | { 74 | for (auto &s : sinks_) 75 | { 76 | if (s->should_log(incoming_log_msg.level)) 77 | { 78 | s->log(incoming_log_msg); 79 | } 80 | } 81 | } 82 | SPDLOG_CATCH_AND_HANDLE 83 | 84 | if (should_flush_(incoming_log_msg)) 85 | { 86 | backend_flush_(); 87 | } 88 | } 89 | 90 | inline void spdlog::async_logger::backend_flush_() 91 | { 92 | try 93 | { 94 | for (auto &sink : sinks_) 95 | { 96 | sink->flush(); 97 | } 98 | } 99 | SPDLOG_CATCH_AND_HANDLE 100 | } 101 | 102 | inline std::shared_ptr spdlog::async_logger::clone(std::string new_name) 103 | { 104 | auto cloned = std::make_shared(std::move(new_name), sinks_.begin(), sinks_.end(), thread_pool_, overflow_policy_); 105 | 106 | cloned->set_level(this->level()); 107 | cloned->flush_on(this->flush_level()); 108 | cloned->set_error_handler(this->error_handler()); 109 | return std::move(cloned); 110 | } 111 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/details/circular_q.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2018 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | // cirucal q view of std::vector. 7 | #pragma once 8 | 9 | #include 10 | 11 | namespace spdlog { 12 | namespace details { 13 | template 14 | class circular_q 15 | { 16 | public: 17 | using item_type = T; 18 | 19 | explicit circular_q(size_t max_items) 20 | : max_items_(max_items + 1) // one item is reserved as marker for full q 21 | , v_(max_items_) 22 | { 23 | } 24 | 25 | // push back, overrun (oldest) item if no room left 26 | void push_back(T &&item) 27 | { 28 | v_[tail_] = std::move(item); 29 | tail_ = (tail_ + 1) % max_items_; 30 | 31 | if (tail_ == head_) // overrun last item if full 32 | { 33 | head_ = (head_ + 1) % max_items_; 34 | ++overrun_counter_; 35 | } 36 | } 37 | 38 | // Pop item from front. 39 | // If there are no elements in the container, the behavior is undefined. 40 | void pop_front(T &popped_item) 41 | { 42 | popped_item = std::move(v_[head_]); 43 | head_ = (head_ + 1) % max_items_; 44 | } 45 | 46 | bool empty() 47 | { 48 | return tail_ == head_; 49 | } 50 | 51 | bool full() 52 | { 53 | // head is ahead of the tail by 1 54 | return ((tail_ + 1) % max_items_) == head_; 55 | } 56 | 57 | size_t overrun_counter() const 58 | { 59 | return overrun_counter_; 60 | } 61 | 62 | private: 63 | size_t max_items_; 64 | typename std::vector::size_type head_ = 0; 65 | typename std::vector::size_type tail_ = 0; 66 | 67 | std::vector v_; 68 | 69 | size_t overrun_counter_ = 0; 70 | }; 71 | } // namespace details 72 | } // namespace spdlog 73 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/details/console_globals.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // 3 | // Copyright(c) 2018 Gabi Melman. 4 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 5 | // 6 | 7 | #include "spdlog/details/null_mutex.h" 8 | #include 9 | #include 10 | 11 | #ifdef _WIN32 12 | 13 | #ifndef NOMINMAX 14 | #define NOMINMAX // prevent windows redefining min/max 15 | #endif 16 | 17 | #ifndef WIN32_LEAN_AND_MEAN 18 | #define WIN32_LEAN_AND_MEAN 19 | #endif 20 | 21 | #include 22 | #endif 23 | 24 | namespace spdlog { 25 | namespace details { 26 | struct console_stdout 27 | { 28 | static std::FILE *stream() 29 | { 30 | return stdout; 31 | } 32 | #ifdef _WIN32 33 | static HANDLE handle() 34 | { 35 | return ::GetStdHandle(STD_OUTPUT_HANDLE); 36 | } 37 | #endif 38 | }; 39 | 40 | struct console_stderr 41 | { 42 | static std::FILE *stream() 43 | { 44 | return stderr; 45 | } 46 | #ifdef _WIN32 47 | static HANDLE handle() 48 | { 49 | return ::GetStdHandle(STD_ERROR_HANDLE); 50 | } 51 | #endif 52 | }; 53 | 54 | struct console_mutex 55 | { 56 | using mutex_t = std::mutex; 57 | static mutex_t &mutex() 58 | { 59 | static mutex_t s_mutex; 60 | return s_mutex; 61 | } 62 | }; 63 | 64 | struct console_nullmutex 65 | { 66 | using mutex_t = null_mutex; 67 | static mutex_t &mutex() 68 | { 69 | static mutex_t s_mutex; 70 | return s_mutex; 71 | } 72 | }; 73 | } // namespace details 74 | } // namespace spdlog 75 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/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 sinks. 9 | // When failing to open a file, retry several times(5) with a delay interval(10 ms). 10 | // Throw spdlog_ex exception on errors. 11 | 12 | #include "spdlog/details/log_msg.h" 13 | #include "spdlog/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 fmt::memory_buffer &buf) 84 | { 85 | size_t msg_size = buf.size(); 86 | auto data = buf.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 130 | // extension 131 | if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1) 132 | { 133 | return std::make_tuple(fname, spdlog::filename_t()); 134 | } 135 | 136 | // treat casese like "/etc/rc.d/somelogfile or "/abc/.hiddenfile" 137 | auto folder_index = fname.rfind(details::os::folder_sep); 138 | if (folder_index != filename_t::npos && folder_index >= ext_index - 1) 139 | { 140 | return std::make_tuple(fname, spdlog::filename_t()); 141 | } 142 | 143 | // finally - return a valid base and extension tuple 144 | return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index)); 145 | } 146 | 147 | private: 148 | std::FILE *fd_{nullptr}; 149 | filename_t _filename; 150 | }; 151 | } // namespace details 152 | } // namespace spdlog 153 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/details/fmt_helper.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by gabi on 6/15/18. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "chrono" 8 | #include "spdlog/fmt/fmt.h" 9 | 10 | // Some fmt helpers to efficiently format and pad ints and strings 11 | namespace spdlog { 12 | namespace details { 13 | namespace fmt_helper { 14 | 15 | template 16 | inline void append_str(const std::string &str, fmt::basic_memory_buffer &dest) 17 | { 18 | auto *str_ptr = str.data(); 19 | dest.append(str_ptr, str_ptr + str.size()); 20 | } 21 | 22 | template 23 | inline void append_c_str(const char *c_str, fmt::basic_memory_buffer &dest) 24 | { 25 | auto len = std::char_traits::length(c_str); 26 | dest.append(c_str, c_str + len); 27 | } 28 | 29 | template 30 | inline void append_buf(const fmt::basic_memory_buffer &buf, fmt::basic_memory_buffer &dest) 31 | { 32 | auto *buf_ptr = buf.data(); 33 | dest.append(buf_ptr, buf_ptr + buf.size()); 34 | } 35 | 36 | template 37 | inline void append_int(T n, fmt::basic_memory_buffer &dest) 38 | { 39 | fmt::format_int i(n); 40 | dest.append(i.data(), i.data() + i.size()); 41 | } 42 | 43 | template 44 | inline void pad2(int n, fmt::basic_memory_buffer &dest) 45 | { 46 | if (n > 99) 47 | { 48 | append_int(n, dest); 49 | return; 50 | } 51 | if (n > 9) // 10-99 52 | { 53 | dest.push_back(static_cast('0' + n / 10)); 54 | dest.push_back(static_cast('0' + n % 10)); 55 | return; 56 | } 57 | if (n >= 0) // 0-9 58 | { 59 | dest.push_back('0'); 60 | dest.push_back(static_cast('0' + n)); 61 | return; 62 | } 63 | // negatives (unlikely, but just in case, let fmt deal with it) 64 | fmt::format_to(dest, "{:02}", n); 65 | } 66 | 67 | template 68 | inline void pad3(int n, fmt::basic_memory_buffer &dest) 69 | { 70 | if (n > 999) 71 | { 72 | append_int(n, dest); 73 | return; 74 | } 75 | 76 | if (n > 99) // 100-999 77 | { 78 | append_int(n / 100, dest); 79 | pad2(n % 100, dest); 80 | return; 81 | } 82 | if (n > 9) // 10-99 83 | { 84 | dest.push_back('0'); 85 | dest.push_back(static_cast('0' + n / 10)); 86 | dest.push_back(static_cast('0' + n % 10)); 87 | return; 88 | } 89 | if (n >= 0) 90 | { 91 | dest.push_back('0'); 92 | dest.push_back('0'); 93 | dest.push_back(static_cast('0' + n)); 94 | return; 95 | } 96 | // negatives (unlikely, but just in case let fmt deal with it) 97 | fmt::format_to(dest, "{:03}", n); 98 | } 99 | 100 | template 101 | inline void pad6(size_t n, fmt::basic_memory_buffer &dest) 102 | { 103 | if (n > 99999) 104 | { 105 | append_int(n, dest); 106 | return; 107 | } 108 | pad3(static_cast(n / 1000), dest); 109 | pad3(static_cast(n % 1000), dest); 110 | } 111 | 112 | // return fraction of a second of the given time_point. 113 | // e.g. 114 | // fraction(tp) -> will return the millis part of the second 115 | template 116 | inline ToDuration time_fraction(const log_clock::time_point &tp) 117 | { 118 | using namespace std::chrono; 119 | auto duration = tp.time_since_epoch(); 120 | auto secs = duration_cast(duration); 121 | return duration_cast(duration) - duration_cast(secs); 122 | } 123 | 124 | } // namespace fmt_helper 125 | } // namespace details 126 | } // namespace spdlog 127 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/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 "spdlog/common.h" 9 | #include "spdlog/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 | 20 | log_msg(const std::string *loggers_name, level::level_enum lvl) 21 | : logger_name(loggers_name) 22 | , level(lvl) 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 | 33 | log_msg(const log_msg &other) = delete; 34 | log_msg(log_msg &&other) = delete; 35 | log_msg &operator=(log_msg &&other) = delete; 36 | 37 | const std::string *logger_name{nullptr}; 38 | level::level_enum level; 39 | log_clock::time_point time; 40 | size_t thread_id; 41 | fmt::memory_buffer raw; 42 | size_t msg_id{0}; 43 | // info about wrapping the formatted text with color 44 | mutable size_t color_range_start{0}; 45 | mutable size_t color_range_end{0}; 46 | }; 47 | } // namespace details 48 | } // namespace spdlog 49 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/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 | // multi producer-multi consumer blocking queue. 9 | // enqueue(..) - will block until room found to put the new message. 10 | // enqueue_nowait(..) - will return immediately with false if no room left in 11 | // the queue. 12 | // dequeue_for(..) - will block until the queue is not empty or timeout have 13 | // passed. 14 | 15 | #include "spdlog/details/circular_q.h" 16 | 17 | #include 18 | #include 19 | 20 | namespace spdlog { 21 | namespace details { 22 | 23 | template 24 | class mpmc_blocking_queue 25 | { 26 | public: 27 | using item_type = T; 28 | explicit mpmc_blocking_queue(size_t max_items) 29 | : q_(max_items) 30 | { 31 | } 32 | 33 | #ifndef __MINGW32__ 34 | // try to enqueue and block if no room left 35 | void enqueue(T &&item) 36 | { 37 | { 38 | std::unique_lock lock(queue_mutex_); 39 | pop_cv_.wait(lock, [this] { return !this->q_.full(); }); 40 | q_.push_back(std::move(item)); 41 | } 42 | push_cv_.notify_one(); 43 | } 44 | 45 | // enqueue immediately. overrun oldest message in the queue if no room left. 46 | void enqueue_nowait(T &&item) 47 | { 48 | { 49 | std::unique_lock lock(queue_mutex_); 50 | q_.push_back(std::move(item)); 51 | } 52 | push_cv_.notify_one(); 53 | } 54 | 55 | // try to dequeue item. if no item found. wait upto timeout and try again 56 | // Return true, if succeeded dequeue item, false otherwise 57 | bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) 58 | { 59 | { 60 | std::unique_lock lock(queue_mutex_); 61 | if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); })) 62 | { 63 | return false; 64 | } 65 | q_.pop_front(popped_item); 66 | } 67 | pop_cv_.notify_one(); 68 | return true; 69 | } 70 | 71 | #else 72 | // apparently mingw deadlocks if the mutex is released before cv.notify_one(), 73 | // so release the mutex at the very end each function. 74 | 75 | // try to enqueue and block if no room left 76 | void enqueue(T &&item) 77 | { 78 | std::unique_lock lock(queue_mutex_); 79 | pop_cv_.wait(lock, [this] { return !this->q_.full(); }); 80 | q_.push_back(std::move(item)); 81 | push_cv_.notify_one(); 82 | } 83 | 84 | // enqueue immediately. overrun oldest message in the queue if no room left. 85 | void enqueue_nowait(T &&item) 86 | { 87 | std::unique_lock lock(queue_mutex_); 88 | q_.push_back(std::move(item)); 89 | push_cv_.notify_one(); 90 | } 91 | 92 | // try to dequeue item. if no item found. wait upto timeout and try again 93 | // Return true, if succeeded dequeue item, false otherwise 94 | bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) 95 | { 96 | std::unique_lock lock(queue_mutex_); 97 | if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); })) 98 | { 99 | return false; 100 | } 101 | q_.pop_front(popped_item); 102 | pop_cv_.notify_one(); 103 | return true; 104 | } 105 | 106 | #endif 107 | 108 | size_t overrun_counter() 109 | { 110 | std::unique_lock lock(queue_mutex_); 111 | return q_.overrun_counter(); 112 | } 113 | 114 | private: 115 | std::mutex queue_mutex_; 116 | std::condition_variable push_cv_; 117 | std::condition_variable pop_cv_; 118 | spdlog::details::circular_q q_; 119 | }; 120 | } // namespace details 121 | } // namespace spdlog 122 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/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 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/details/periodic_worker.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright(c) 2018 Gabi Melman. 4 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 5 | // 6 | 7 | #pragma once 8 | 9 | // periodic worker thread - periodically executes the given callback function. 10 | // 11 | // RAII over the owned thread: 12 | // creates the thread on construction. 13 | // stops and joins the thread on destruction. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | namespace spdlog { 21 | namespace details { 22 | 23 | class periodic_worker 24 | { 25 | public: 26 | periodic_worker(const std::function &callback_fun, std::chrono::seconds interval) 27 | { 28 | active_ = (interval > std::chrono::seconds::zero()); 29 | if (!active_) 30 | { 31 | return; 32 | } 33 | 34 | worker_thread_ = std::thread([this, callback_fun, interval]() { 35 | for (;;) 36 | { 37 | std::unique_lock lock(this->mutex_); 38 | if (this->cv_.wait_for(lock, interval, [this] { return !this->active_; })) 39 | { 40 | return; // active_ == false, so exit this thread 41 | } 42 | callback_fun(); 43 | } 44 | }); 45 | } 46 | 47 | periodic_worker(const periodic_worker &) = delete; 48 | periodic_worker &operator=(const periodic_worker &) = delete; 49 | 50 | // stop the worker thread and join it 51 | ~periodic_worker() 52 | { 53 | if (worker_thread_.joinable()) 54 | { 55 | { 56 | std::lock_guard lock(mutex_); 57 | active_ = false; 58 | } 59 | cv_.notify_one(); 60 | worker_thread_.join(); 61 | } 62 | } 63 | 64 | private: 65 | bool active_; 66 | std::thread worker_thread_; 67 | std::mutex mutex_; 68 | std::condition_variable cv_; 69 | }; 70 | } // namespace details 71 | } // namespace spdlog 72 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/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 "spdlog/common.h" 14 | #include "spdlog/details/periodic_worker.h" 15 | #include "spdlog/logger.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace spdlog { 24 | namespace details { 25 | class thread_pool; 26 | 27 | class registry 28 | { 29 | public: 30 | registry(const registry &) = delete; 31 | registry &operator=(const registry &) = delete; 32 | 33 | void register_logger(std::shared_ptr new_logger) 34 | { 35 | std::lock_guard lock(logger_map_mutex_); 36 | auto logger_name = new_logger->name(); 37 | throw_if_exists_(logger_name); 38 | loggers_[logger_name] = std::move(new_logger); 39 | } 40 | 41 | void register_and_init(std::shared_ptr new_logger) 42 | { 43 | std::lock_guard lock(logger_map_mutex_); 44 | auto logger_name = new_logger->name(); 45 | throw_if_exists_(logger_name); 46 | 47 | // set the global formatter pattern 48 | new_logger->set_formatter(formatter_->clone()); 49 | 50 | if (err_handler_) 51 | { 52 | new_logger->set_error_handler(err_handler_); 53 | } 54 | 55 | new_logger->set_level(level_); 56 | new_logger->flush_on(flush_level_); 57 | 58 | // add to registry 59 | loggers_[logger_name] = std::move(new_logger); 60 | } 61 | 62 | std::shared_ptr get(const std::string &logger_name) 63 | { 64 | std::lock_guard lock(logger_map_mutex_); 65 | auto found = loggers_.find(logger_name); 66 | return found == loggers_.end() ? nullptr : found->second; 67 | } 68 | 69 | void set_tp(std::shared_ptr tp) 70 | { 71 | std::lock_guard lock(tp_mutex_); 72 | tp_ = std::move(tp); 73 | } 74 | 75 | std::shared_ptr get_tp() 76 | { 77 | std::lock_guard lock(tp_mutex_); 78 | return tp_; 79 | } 80 | 81 | // Set global formatter. Each sink in each logger will get a clone of this object 82 | void set_formatter(std::unique_ptr formatter) 83 | { 84 | std::lock_guard lock(logger_map_mutex_); 85 | formatter_ = std::move(formatter); 86 | for (auto &l : loggers_) 87 | { 88 | l.second->set_formatter(formatter_->clone()); 89 | } 90 | } 91 | 92 | void set_level(level::level_enum log_level) 93 | { 94 | std::lock_guard lock(logger_map_mutex_); 95 | for (auto &l : loggers_) 96 | { 97 | l.second->set_level(log_level); 98 | } 99 | level_ = log_level; 100 | } 101 | 102 | void flush_on(level::level_enum log_level) 103 | { 104 | std::lock_guard lock(logger_map_mutex_); 105 | for (auto &l : loggers_) 106 | { 107 | l.second->flush_on(log_level); 108 | } 109 | flush_level_ = log_level; 110 | } 111 | 112 | void flush_every(std::chrono::seconds interval) 113 | { 114 | std::lock_guard lock(flusher_mutex_); 115 | std::function clbk = std::bind(®istry::flush_all, this); 116 | periodic_flusher_ = spdlog::make_unique(clbk, interval); 117 | } 118 | 119 | void set_error_handler(log_err_handler handler) 120 | { 121 | std::lock_guard lock(logger_map_mutex_); 122 | for (auto &l : loggers_) 123 | { 124 | l.second->set_error_handler(handler); 125 | } 126 | err_handler_ = handler; 127 | } 128 | 129 | void apply_all(const std::function)> &fun) 130 | { 131 | std::lock_guard lock(logger_map_mutex_); 132 | for (auto &l : loggers_) 133 | { 134 | fun(l.second); 135 | } 136 | } 137 | 138 | void flush_all() 139 | { 140 | std::lock_guard lock(logger_map_mutex_); 141 | for (auto &l : loggers_) 142 | { 143 | l.second->flush(); 144 | } 145 | } 146 | 147 | void drop(const std::string &logger_name) 148 | { 149 | std::lock_guard lock(logger_map_mutex_); 150 | loggers_.erase(logger_name); 151 | } 152 | 153 | void drop_all() 154 | { 155 | std::lock_guard lock(logger_map_mutex_); 156 | loggers_.clear(); 157 | } 158 | 159 | // clean all reasources and threads started by the registry 160 | void shutdown() 161 | { 162 | { 163 | std::lock_guard lock(flusher_mutex_); 164 | periodic_flusher_.reset(); 165 | } 166 | 167 | drop_all(); 168 | 169 | { 170 | std::lock_guard lock(tp_mutex_); 171 | tp_.reset(); 172 | } 173 | } 174 | 175 | std::recursive_mutex &tp_mutex() 176 | { 177 | return tp_mutex_; 178 | } 179 | 180 | static registry &instance() 181 | { 182 | static registry s_instance; 183 | return s_instance; 184 | } 185 | 186 | private: 187 | registry() 188 | : formatter_(new pattern_formatter("%+")) 189 | { 190 | } 191 | 192 | ~registry() = default; 193 | 194 | void throw_if_exists_(const std::string &logger_name) 195 | { 196 | if (loggers_.find(logger_name) != loggers_.end()) 197 | { 198 | throw spdlog_ex("logger with name '" + logger_name + "' already exists"); 199 | } 200 | } 201 | 202 | std::mutex logger_map_mutex_, flusher_mutex_; 203 | std::recursive_mutex tp_mutex_; 204 | std::unordered_map> loggers_; 205 | std::unique_ptr formatter_; 206 | level::level_enum level_ = level::info; 207 | level::level_enum flush_level_ = level::off; 208 | log_err_handler err_handler_; 209 | std::shared_ptr tp_; 210 | std::unique_ptr periodic_flusher_; 211 | }; 212 | 213 | } // namespace details 214 | } // namespace spdlog 215 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/details/thread_pool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spdlog/details/log_msg.h" 4 | #include "spdlog/details/mpmc_blocking_q.h" 5 | #include "spdlog/details/os.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace spdlog { 13 | namespace details { 14 | 15 | using async_logger_ptr = std::shared_ptr; 16 | 17 | enum class async_msg_type 18 | { 19 | log, 20 | flush, 21 | terminate 22 | }; 23 | 24 | // Async msg to move to/from the queue 25 | // Movable only. should never be copied 26 | struct async_msg 27 | { 28 | async_msg_type msg_type; 29 | level::level_enum level; 30 | log_clock::time_point time; 31 | size_t thread_id; 32 | fmt::basic_memory_buffer raw; 33 | 34 | size_t msg_id; 35 | async_logger_ptr worker_ptr; 36 | 37 | async_msg() = default; 38 | ~async_msg() = default; 39 | 40 | // should only be moved in or out of the queue.. 41 | async_msg(const async_msg &) = delete; 42 | 43 | // support for vs2013 move 44 | #if defined(_MSC_VER) && _MSC_VER <= 1800 45 | async_msg(async_msg &&other) SPDLOG_NOEXCEPT : msg_type(other.msg_type), 46 | level(other.level), 47 | time(other.time), 48 | thread_id(other.thread_id), 49 | raw(move(other.raw)), 50 | msg_id(other.msg_id), 51 | worker_ptr(std::move(other.worker_ptr)) 52 | { 53 | } 54 | 55 | async_msg &operator=(async_msg &&other) SPDLOG_NOEXCEPT 56 | { 57 | msg_type = other.msg_type; 58 | level = other.level; 59 | time = other.time; 60 | thread_id = other.thread_id; 61 | raw = std::move(other.raw); 62 | msg_id = other.msg_id; 63 | worker_ptr = std::move(other.worker_ptr); 64 | return *this; 65 | } 66 | #else // (_MSC_VER) && _MSC_VER <= 1800 67 | async_msg(async_msg &&) = default; 68 | async_msg &operator=(async_msg &&) = default; 69 | #endif 70 | 71 | // construct from log_msg with given type 72 | async_msg(async_logger_ptr &&worker, async_msg_type the_type, details::log_msg &&m) 73 | : msg_type(the_type) 74 | , level(m.level) 75 | , time(m.time) 76 | , thread_id(m.thread_id) 77 | , msg_id(m.msg_id) 78 | , worker_ptr(std::forward(worker)) 79 | { 80 | fmt_helper::append_buf(m.raw, raw); 81 | } 82 | 83 | async_msg(async_logger_ptr &&worker, async_msg_type the_type) 84 | : async_msg(std::forward(worker), the_type, details::log_msg()) 85 | { 86 | } 87 | 88 | explicit async_msg(async_msg_type the_type) 89 | : async_msg(nullptr, the_type, details::log_msg()) 90 | { 91 | } 92 | 93 | // copy into log_msg 94 | void to_log_msg(log_msg &msg) 95 | { 96 | msg.logger_name = &worker_ptr->name(); 97 | msg.level = level; 98 | msg.time = time; 99 | msg.thread_id = thread_id; 100 | fmt_helper::append_buf(raw, msg.raw); 101 | msg.msg_id = msg_id; 102 | msg.color_range_start = 0; 103 | msg.color_range_end = 0; 104 | } 105 | }; 106 | 107 | class thread_pool 108 | { 109 | public: 110 | using item_type = async_msg; 111 | using q_type = details::mpmc_blocking_queue; 112 | 113 | thread_pool(size_t q_max_items, size_t threads_n) 114 | : q_(q_max_items) 115 | { 116 | // std::cout << "thread_pool() q_size_bytes: " << q_size_bytes << 117 | // "\tthreads_n: " << threads_n << std::endl; 118 | if (threads_n == 0 || threads_n > 1000) 119 | { 120 | throw spdlog_ex("spdlog::thread_pool(): invalid threads_n param (valid " 121 | "range is 1-1000)"); 122 | } 123 | for (size_t i = 0; i < threads_n; i++) 124 | { 125 | threads_.emplace_back(&thread_pool::worker_loop_, this); 126 | } 127 | } 128 | 129 | // message all threads to terminate gracefully join them 130 | ~thread_pool() 131 | { 132 | try 133 | { 134 | for (size_t i = 0; i < threads_.size(); i++) 135 | { 136 | post_async_msg_(async_msg(async_msg_type::terminate), async_overflow_policy::block); 137 | } 138 | 139 | for (auto &t : threads_) 140 | { 141 | t.join(); 142 | } 143 | } 144 | catch (...) 145 | { 146 | } 147 | } 148 | 149 | void post_log(async_logger_ptr &&worker_ptr, details::log_msg &&msg, async_overflow_policy overflow_policy) 150 | { 151 | async_msg async_m(std::forward(worker_ptr), async_msg_type::log, std::forward(msg)); 152 | post_async_msg_(std::move(async_m), overflow_policy); 153 | } 154 | 155 | void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy) 156 | { 157 | post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy); 158 | } 159 | 160 | size_t overrun_counter() 161 | { 162 | return q_.overrun_counter(); 163 | } 164 | 165 | private: 166 | q_type q_; 167 | 168 | std::vector threads_; 169 | 170 | void post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy) 171 | { 172 | if (overflow_policy == async_overflow_policy::block) 173 | { 174 | q_.enqueue(std::move(new_msg)); 175 | } 176 | else 177 | { 178 | q_.enqueue_nowait(std::move(new_msg)); 179 | } 180 | } 181 | 182 | void worker_loop_() 183 | { 184 | while (process_next_msg_()) {}; 185 | } 186 | 187 | // process next message in the queue 188 | // return true if this thread should still be active (while no terminate msg 189 | // was received) 190 | bool process_next_msg_() 191 | { 192 | async_msg incoming_async_msg; 193 | bool dequeued = q_.dequeue_for(incoming_async_msg, std::chrono::seconds(10)); 194 | if (!dequeued) 195 | { 196 | return true; 197 | } 198 | 199 | switch (incoming_async_msg.msg_type) 200 | { 201 | case async_msg_type::log: 202 | { 203 | log_msg msg; 204 | incoming_async_msg.to_log_msg(msg); 205 | incoming_async_msg.worker_ptr->backend_log_(msg); 206 | return true; 207 | } 208 | case async_msg_type::flush: 209 | { 210 | incoming_async_msg.worker_ptr->backend_flush_(); 211 | return true; 212 | } 213 | 214 | case async_msg_type::terminate: 215 | { 216 | return false; 217 | } 218 | } 219 | assert(false && "Unexpected async_msg_type"); 220 | return true; 221 | } 222 | }; 223 | 224 | } // namespace details 225 | } // namespace spdlog 226 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/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 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/fmt/bundled/locale.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - locale support 2 | // 3 | // Copyright (c) 2012 - 2016, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #include "format.h" 9 | #include 10 | 11 | namespace fmt { 12 | class locale 13 | { 14 | private: 15 | std::locale locale_; 16 | 17 | public: 18 | explicit locale(std::locale loc = std::locale()) 19 | : locale_(loc) 20 | { 21 | } 22 | std::locale get() 23 | { 24 | return locale_; 25 | } 26 | }; 27 | } // namespace fmt 28 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/fmt/bundled/ostream.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - std::ostream support 2 | // 3 | // Copyright (c) 2012 - 2016, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_OSTREAM_H_ 9 | #define FMT_OSTREAM_H_ 10 | 11 | #include "format.h" 12 | #include 13 | 14 | FMT_BEGIN_NAMESPACE 15 | namespace internal { 16 | 17 | template 18 | class formatbuf : public std::basic_streambuf 19 | { 20 | private: 21 | typedef typename std::basic_streambuf::int_type int_type; 22 | typedef typename std::basic_streambuf::traits_type traits_type; 23 | 24 | basic_buffer &buffer_; 25 | 26 | public: 27 | formatbuf(basic_buffer &buffer) 28 | : buffer_(buffer) 29 | { 30 | } 31 | 32 | protected: 33 | // The put-area is actually always empty. This makes the implementation 34 | // simpler and has the advantage that the streambuf and the buffer are always 35 | // in sync and sputc never writes into uninitialized memory. The obvious 36 | // disadvantage is that each call to sputc always results in a (virtual) call 37 | // to overflow. There is no disadvantage here for sputn since this always 38 | // results in a call to xsputn. 39 | 40 | int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE 41 | { 42 | if (!traits_type::eq_int_type(ch, traits_type::eof())) 43 | buffer_.push_back(static_cast(ch)); 44 | return ch; 45 | } 46 | 47 | std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE 48 | { 49 | buffer_.append(s, s + count); 50 | return count; 51 | } 52 | }; 53 | 54 | template 55 | struct test_stream : std::basic_ostream 56 | { 57 | private: 58 | struct null; 59 | // Hide all operator<< from std::basic_ostream. 60 | void operator<<(null); 61 | }; 62 | 63 | // Checks if T has a user-defined operator<< (e.g. not a member of 64 | // std::ostream). 65 | template 66 | class is_streamable 67 | { 68 | private: 69 | template 70 | static decltype(internal::declval &>() << internal::declval(), std::true_type()) test(int); 71 | 72 | template 73 | static std::false_type test(...); 74 | 75 | typedef decltype(test(0)) result; 76 | 77 | public: 78 | // std::string operator<< is not considered user-defined because we handle 79 | // strings 80 | // specially. 81 | static const bool value = result::value && !std::is_same::value; 82 | }; 83 | 84 | // Disable conversion to int if T has an overloaded operator<< which is a free 85 | // function (not a member of std::ostream). 86 | template 87 | class convert_to_int 88 | { 89 | public: 90 | static const bool value = convert_to_int::value && !is_streamable::value; 91 | }; 92 | 93 | // Write the content of buf to os. 94 | template 95 | void write(std::basic_ostream &os, basic_buffer &buf) 96 | { 97 | const Char *data = buf.data(); 98 | typedef std::make_unsigned::type UnsignedStreamSize; 99 | UnsignedStreamSize size = buf.size(); 100 | UnsignedStreamSize max_size = internal::to_unsigned((std::numeric_limits::max)()); 101 | do 102 | { 103 | UnsignedStreamSize n = size <= max_size ? size : max_size; 104 | os.write(data, static_cast(n)); 105 | data += n; 106 | size -= n; 107 | } while (size != 0); 108 | } 109 | 110 | template 111 | void format_value(basic_buffer &buffer, const T &value) 112 | { 113 | internal::formatbuf format_buf(buffer); 114 | std::basic_ostream output(&format_buf); 115 | output.exceptions(std::ios_base::failbit | std::ios_base::badbit); 116 | output << value; 117 | buffer.resize(buffer.size()); 118 | } 119 | 120 | // Disable builtin formatting of enums and use operator<< instead. 121 | template 122 | struct format_enum::value>::type> : std::false_type 123 | { 124 | }; 125 | } // namespace internal 126 | 127 | // Formats an object of type T that has an overloaded ostream operator<<. 128 | template 129 | struct formatter::value>::type> : formatter, Char> 130 | { 131 | 132 | template 133 | auto format(const T &value, Context &ctx) -> decltype(ctx.out()) 134 | { 135 | basic_memory_buffer buffer; 136 | internal::format_value(buffer, value); 137 | basic_string_view str(buffer.data(), buffer.size()); 138 | formatter, Char>::format(str, ctx); 139 | return ctx.out(); 140 | } 141 | }; 142 | 143 | template 144 | inline void vprint( 145 | std::basic_ostream &os, basic_string_view format_str, basic_format_args::type> args) 146 | { 147 | basic_memory_buffer buffer; 148 | vformat_to(buffer, format_str, args); 149 | internal::write(os, buffer); 150 | } 151 | /** 152 | \rst 153 | Prints formatted data to the stream *os*. 154 | 155 | **Example**:: 156 | 157 | fmt::print(cerr, "Don't {}!", "panic"); 158 | \endrst 159 | */ 160 | template 161 | inline void print(std::ostream &os, string_view format_str, const Args &... args) 162 | { 163 | vprint(os, format_str, make_format_args(args...)); 164 | } 165 | 166 | template 167 | inline void print(std::wostream &os, wstring_view format_str, const Args &... args) 168 | { 169 | vprint(os, format_str, make_format_args(args...)); 170 | } 171 | FMT_END_NAMESPACE 172 | 173 | #endif // FMT_OSTREAM_H_ 174 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/fmt/bundled/time.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - time formatting 2 | // 3 | // Copyright (c) 2012 - 2016, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_TIME_H_ 9 | #define FMT_TIME_H_ 10 | 11 | #include "format.h" 12 | #include 13 | 14 | FMT_BEGIN_NAMESPACE 15 | 16 | namespace internal { 17 | inline null<> localtime_r(...) 18 | { 19 | return null<>(); 20 | } 21 | inline null<> localtime_s(...) 22 | { 23 | return null<>(); 24 | } 25 | inline null<> gmtime_r(...) 26 | { 27 | return null<>(); 28 | } 29 | inline null<> gmtime_s(...) 30 | { 31 | return null<>(); 32 | } 33 | } // namespace internal 34 | 35 | // Thread-safe replacement for std::localtime 36 | inline std::tm localtime(std::time_t time) 37 | { 38 | struct dispatcher 39 | { 40 | std::time_t time_; 41 | std::tm tm_; 42 | 43 | dispatcher(std::time_t t) 44 | : time_(t) 45 | { 46 | } 47 | 48 | bool run() 49 | { 50 | using namespace fmt::internal; 51 | return handle(localtime_r(&time_, &tm_)); 52 | } 53 | 54 | bool handle(std::tm *tm) 55 | { 56 | return tm != FMT_NULL; 57 | } 58 | 59 | bool handle(internal::null<>) 60 | { 61 | using namespace fmt::internal; 62 | return fallback(localtime_s(&tm_, &time_)); 63 | } 64 | 65 | bool fallback(int res) 66 | { 67 | return res == 0; 68 | } 69 | 70 | bool fallback(internal::null<>) 71 | { 72 | using namespace fmt::internal; 73 | std::tm *tm = std::localtime(&time_); 74 | if (tm) 75 | tm_ = *tm; 76 | return tm != FMT_NULL; 77 | } 78 | }; 79 | dispatcher lt(time); 80 | if (lt.run()) 81 | return lt.tm_; 82 | // Too big time values may be unsupported. 83 | FMT_THROW(format_error("time_t value out of range")); 84 | } 85 | 86 | // Thread-safe replacement for std::gmtime 87 | inline std::tm gmtime(std::time_t time) 88 | { 89 | struct dispatcher 90 | { 91 | std::time_t time_; 92 | std::tm tm_; 93 | 94 | dispatcher(std::time_t t) 95 | : time_(t) 96 | { 97 | } 98 | 99 | bool run() 100 | { 101 | using namespace fmt::internal; 102 | return handle(gmtime_r(&time_, &tm_)); 103 | } 104 | 105 | bool handle(std::tm *tm) 106 | { 107 | return tm != FMT_NULL; 108 | } 109 | 110 | bool handle(internal::null<>) 111 | { 112 | using namespace fmt::internal; 113 | return fallback(gmtime_s(&tm_, &time_)); 114 | } 115 | 116 | bool fallback(int res) 117 | { 118 | return res == 0; 119 | } 120 | 121 | bool fallback(internal::null<>) 122 | { 123 | std::tm *tm = std::gmtime(&time_); 124 | if (tm) 125 | tm_ = *tm; 126 | return tm != FMT_NULL; 127 | } 128 | }; 129 | dispatcher gt(time); 130 | if (gt.run()) 131 | return gt.tm_; 132 | // Too big time values may be unsupported. 133 | FMT_THROW(format_error("time_t value out of range")); 134 | } 135 | 136 | namespace internal { 137 | inline std::size_t strftime(char *str, std::size_t count, const char *format, const std::tm *time) 138 | { 139 | return std::strftime(str, count, format, time); 140 | } 141 | 142 | inline std::size_t strftime(wchar_t *str, std::size_t count, const wchar_t *format, const std::tm *time) 143 | { 144 | return std::wcsftime(str, count, format, time); 145 | } 146 | } // namespace internal 147 | 148 | template 149 | struct formatter 150 | { 151 | template 152 | auto parse(ParseContext &ctx) -> decltype(ctx.begin()) 153 | { 154 | auto it = internal::null_terminating_iterator(ctx); 155 | if (*it == ':') 156 | ++it; 157 | auto end = it; 158 | while (*end && *end != '}') 159 | ++end; 160 | tm_format.reserve(end - it + 1); 161 | using internal::pointer_from; 162 | tm_format.append(pointer_from(it), pointer_from(end)); 163 | tm_format.push_back('\0'); 164 | return pointer_from(end); 165 | } 166 | 167 | template 168 | auto format(const std::tm &tm, FormatContext &ctx) -> decltype(ctx.out()) 169 | { 170 | internal::basic_buffer &buf = internal::get_container(ctx.out()); 171 | std::size_t start = buf.size(); 172 | for (;;) 173 | { 174 | std::size_t size = buf.capacity() - start; 175 | std::size_t count = internal::strftime(&buf[start], size, &tm_format[0], &tm); 176 | if (count != 0) 177 | { 178 | buf.resize(start + count); 179 | break; 180 | } 181 | if (size >= tm_format.size() * 256) 182 | { 183 | // If the buffer is 256 times larger than the format string, assume 184 | // that `strftime` gives an empty result. There doesn't seem to be a 185 | // better way to distinguish the two cases: 186 | // https://github.com/fmtlib/fmt/issues/367 187 | break; 188 | } 189 | const std::size_t MIN_GROWTH = 10; 190 | buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); 191 | } 192 | return ctx.out(); 193 | } 194 | 195 | basic_memory_buffer tm_format; 196 | }; 197 | FMT_END_NAMESPACE 198 | 199 | #endif // FMT_TIME_H_ 200 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/fmt/fmt.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2016-2018 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 | #ifndef FMT_HEADER_ONLY 15 | #define FMT_HEADER_ONLY 16 | #endif 17 | #ifndef FMT_USE_WINDOWS_H 18 | #define FMT_USE_WINDOWS_H 0 19 | #endif 20 | #include "bundled/core.h" 21 | #include "bundled/format.h" 22 | #else // external fmtlib 23 | #include 24 | #include 25 | #endif 26 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/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 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/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 "fmt/fmt.h" 9 | #include "spdlog/details/log_msg.h" 10 | 11 | namespace spdlog { 12 | 13 | class formatter 14 | { 15 | public: 16 | virtual ~formatter() = default; 17 | virtual void format(const details::log_msg &msg, fmt::memory_buffer &dest) = 0; 18 | virtual std::unique_ptr clone() const = 0; 19 | }; 20 | } // namespace spdlog 21 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/logger.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015-2108 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 9 | // set_error_handler()) 10 | // Has name, log level, vector of std::shared sink pointers and formatter 11 | // Upon each log write the logger: 12 | // 1. Checks if its log level is enough to log the message and if yes: 13 | // 2. Call the underlying sinks to do the job. 14 | // 3. Each sink use its own private copy of a formatter to format the message 15 | // and send to its destination. 16 | // 17 | // The use of private formatter per sink provides the opportunity to cache some 18 | // formatted data, 19 | // and support customize format per each sink. 20 | 21 | #include "spdlog/common.h" 22 | #include "spdlog/formatter.h" 23 | #include "spdlog/sinks/sink.h" 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | namespace spdlog { 30 | 31 | class logger 32 | { 33 | public: 34 | logger(std::string name, sink_ptr single_sink); 35 | logger(std::string name, sinks_init_list sinks); 36 | 37 | template 38 | logger(std::string name, const It &begin, const It &end); 39 | 40 | virtual ~logger(); 41 | 42 | logger(const logger &) = delete; 43 | logger &operator=(const logger &) = delete; 44 | 45 | template 46 | void log(level::level_enum lvl, const char *fmt, const Args &... args); 47 | 48 | template 49 | void log(level::level_enum lvl, const char *msg); 50 | 51 | template 52 | void trace(const char *fmt, const Args &... args); 53 | 54 | template 55 | void debug(const char *fmt, const Args &... args); 56 | 57 | template 58 | void info(const char *fmt, const Args &... args); 59 | 60 | template 61 | void warn(const char *fmt, const Args &... args); 62 | 63 | template 64 | void error(const char *fmt, const Args &... args); 65 | 66 | template 67 | void critical(const char *fmt, const Args &... args); 68 | 69 | #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT 70 | template 71 | void log(level::level_enum lvl, const wchar_t *fmt, const Args &... args); 72 | 73 | template 74 | void trace(const wchar_t *fmt, const Args &... args); 75 | 76 | template 77 | void debug(const wchar_t *fmt, const Args &... args); 78 | 79 | template 80 | void info(const wchar_t *fmt, const Args &... args); 81 | 82 | template 83 | void warn(const wchar_t *fmt, const Args &... args); 84 | 85 | template 86 | void error(const wchar_t *fmt, const Args &... args); 87 | 88 | template 89 | void critical(const wchar_t *fmt, const Args &... args); 90 | #endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT 91 | 92 | template 93 | void log(level::level_enum lvl, const T &); 94 | 95 | template 96 | void trace(const T &msg); 97 | 98 | template 99 | void debug(const T &msg); 100 | 101 | template 102 | void info(const T &msg); 103 | 104 | template 105 | void warn(const T &msg); 106 | 107 | template 108 | void error(const T &msg); 109 | 110 | template 111 | void critical(const T &msg); 112 | 113 | bool should_log(level::level_enum msg_level) const; 114 | void set_level(level::level_enum log_level); 115 | level::level_enum level() const; 116 | const std::string &name() const; 117 | 118 | // set formatting for the sinks in this logger. 119 | // each sink will get a seperate instance of the formatter object. 120 | void set_formatter(std::unique_ptr formatter); 121 | void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local); 122 | 123 | // flush functions 124 | void flush(); 125 | void flush_on(level::level_enum log_level); 126 | level::level_enum flush_level() const; 127 | 128 | // sinks 129 | const std::vector &sinks() const; 130 | std::vector &sinks(); 131 | 132 | // error handler 133 | void set_error_handler(log_err_handler err_handler); 134 | log_err_handler error_handler(); 135 | 136 | // create new logger with same sinks and configuration. 137 | virtual std::shared_ptr clone(std::string logger_name); 138 | 139 | protected: 140 | virtual void sink_it_(details::log_msg &msg); 141 | virtual void flush_(); 142 | 143 | bool should_flush_(const details::log_msg &msg); 144 | 145 | // default error handler: print the error to stderr with the max rate of 1 146 | // message/minute 147 | void default_err_handler_(const std::string &msg); 148 | 149 | // increment the message count (only if 150 | // defined(SPDLOG_ENABLE_MESSAGE_COUNTER)) 151 | void incr_msg_counter_(details::log_msg &msg); 152 | 153 | const std::string name_; 154 | std::vector sinks_; 155 | spdlog::level_t level_; 156 | spdlog::level_t flush_level_; 157 | log_err_handler err_handler_; 158 | std::atomic last_err_time_; 159 | std::atomic msg_counter_; 160 | 161 | #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT 162 | std::wstring_convert> wstring_converter_; 163 | std::mutex wstring_converter_mutex_; 164 | #endif 165 | }; 166 | } // namespace spdlog 167 | 168 | #include "details/logger_impl.h" 169 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/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 | #include "spdlog/details/fmt_helper.h" 9 | #include "spdlog/details/null_mutex.h" 10 | #include "spdlog/details/os.h" 11 | #include "spdlog/sinks/base_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 | */ 29 | template 30 | class android_sink SPDLOG_FINAL : public base_sink 31 | { 32 | public: 33 | explicit android_sink(std::string tag = "spdlog", bool use_raw_msg = false) 34 | : tag_(std::move(tag)) 35 | , use_raw_msg_(use_raw_msg) 36 | { 37 | } 38 | 39 | protected: 40 | void sink_it_(const details::log_msg &msg) override 41 | { 42 | const android_LogPriority priority = convert_to_android_(msg.level); 43 | fmt::memory_buffer formatted; 44 | if (use_raw_msg_) 45 | { 46 | details::fmt_helper::append_buf(msg.raw, formatted); 47 | } 48 | else 49 | { 50 | sink::formatter_->format(msg, formatted); 51 | } 52 | formatted.push_back('\0'); 53 | const char *msg_output = formatted.data(); 54 | 55 | // See system/core/liblog/logger_write.c for explanation of return value 56 | int ret = __android_log_write(priority, tag_.c_str(), msg_output); 57 | int retry_count = 0; 58 | while ((ret == -11 /*EAGAIN*/) && (retry_count < SPDLOG_ANDROID_RETRIES)) 59 | { 60 | details::os::sleep_for_millis(5); 61 | ret = __android_log_write(priority, tag_.c_str(), msg_output); 62 | retry_count++; 63 | } 64 | 65 | if (ret < 0) 66 | { 67 | throw spdlog_ex("__android_log_write() failed", ret); 68 | } 69 | } 70 | 71 | void flush_() override {} 72 | 73 | private: 74 | static android_LogPriority convert_to_android_(spdlog::level::level_enum level) 75 | { 76 | switch (level) 77 | { 78 | case spdlog::level::trace: 79 | return ANDROID_LOG_VERBOSE; 80 | case spdlog::level::debug: 81 | return ANDROID_LOG_DEBUG; 82 | case spdlog::level::info: 83 | return ANDROID_LOG_INFO; 84 | case spdlog::level::warn: 85 | return ANDROID_LOG_WARN; 86 | case spdlog::level::err: 87 | return ANDROID_LOG_ERROR; 88 | case spdlog::level::critical: 89 | return ANDROID_LOG_FATAL; 90 | default: 91 | return ANDROID_LOG_DEFAULT; 92 | } 93 | } 94 | 95 | std::string tag_; 96 | bool use_raw_msg_; 97 | }; 98 | 99 | using android_sink_mt = android_sink; 100 | using android_sink_st = android_sink; 101 | } // namespace sinks 102 | 103 | // Create and register android syslog logger 104 | 105 | template 106 | inline std::shared_ptr android_logger_mt(const std::string &logger_name, const std::string &tag = "spdlog") 107 | { 108 | return Factory::template create(logger_name, tag); 109 | } 110 | 111 | template 112 | inline std::shared_ptr android_logger_st(const std::string &logger_name, const std::string &tag = "spdlog") 113 | { 114 | return Factory::template create(logger_name, tag); 115 | } 116 | 117 | } // namespace spdlog 118 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/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 "spdlog/details/console_globals.h" 9 | #include "spdlog/details/null_mutex.h" 10 | #include "spdlog/details/os.h" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace spdlog { 18 | namespace sinks { 19 | 20 | /** 21 | * This sink prefixes the output with an ANSI escape sequence color code 22 | * depending on the severity 23 | * of the message. 24 | * If no color terminal detected, omit the escape codes. 25 | */ 26 | template 27 | class ansicolor_sink SPDLOG_FINAL : public sink 28 | { 29 | public: 30 | using mutex_t = typename ConsoleMutex::mutex_t; 31 | ansicolor_sink() 32 | : target_file_(TargetStream::stream()) 33 | , mutex_(ConsoleMutex::mutex()) 34 | 35 | { 36 | should_do_colors_ = details::os::in_terminal(target_file_) && details::os::is_color_terminal(); 37 | colors_[level::trace] = white; 38 | colors_[level::debug] = cyan; 39 | colors_[level::info] = green; 40 | colors_[level::warn] = yellow + bold; 41 | colors_[level::err] = red + bold; 42 | colors_[level::critical] = bold + on_red; 43 | colors_[level::off] = reset; 44 | } 45 | 46 | ~ansicolor_sink() override = default; 47 | 48 | ansicolor_sink(const ansicolor_sink &other) = delete; 49 | ansicolor_sink &operator=(const ansicolor_sink &other) = delete; 50 | 51 | void set_color(level::level_enum color_level, const std::string &color) 52 | { 53 | std::lock_guard lock(mutex_); 54 | colors_[color_level] = color; 55 | } 56 | 57 | /// Formatting codes 58 | const std::string reset = "\033[m"; 59 | const std::string bold = "\033[1m"; 60 | const std::string dark = "\033[2m"; 61 | const std::string underline = "\033[4m"; 62 | const std::string blink = "\033[5m"; 63 | const std::string reverse = "\033[7m"; 64 | const std::string concealed = "\033[8m"; 65 | const std::string clear_line = "\033[K"; 66 | 67 | // Foreground colors 68 | const std::string black = "\033[30m"; 69 | const std::string red = "\033[31m"; 70 | const std::string green = "\033[32m"; 71 | const std::string yellow = "\033[33m"; 72 | const std::string blue = "\033[34m"; 73 | const std::string magenta = "\033[35m"; 74 | const std::string cyan = "\033[36m"; 75 | const std::string white = "\033[37m"; 76 | 77 | /// Background colors 78 | const std::string on_black = "\033[40m"; 79 | const std::string on_red = "\033[41m"; 80 | const std::string on_green = "\033[42m"; 81 | const std::string on_yellow = "\033[43m"; 82 | const std::string on_blue = "\033[44m"; 83 | const std::string on_magenta = "\033[45m"; 84 | const std::string on_cyan = "\033[46m"; 85 | const std::string on_white = "\033[47m"; 86 | 87 | void log(const details::log_msg &msg) override 88 | { 89 | // Wrap the originally formatted message in color codes. 90 | // If color is not supported in the terminal, log as is instead. 91 | std::lock_guard lock(mutex_); 92 | 93 | fmt::memory_buffer formatted; 94 | formatter_->format(msg, formatted); 95 | if (should_do_colors_ && msg.color_range_end > msg.color_range_start) 96 | { 97 | // before color range 98 | print_range_(formatted, 0, msg.color_range_start); 99 | // in color range 100 | print_ccode_(colors_[msg.level]); 101 | print_range_(formatted, msg.color_range_start, msg.color_range_end); 102 | print_ccode_(reset); 103 | // after color range 104 | print_range_(formatted, msg.color_range_end, formatted.size()); 105 | } 106 | else // no color 107 | { 108 | print_range_(formatted, 0, formatted.size()); 109 | } 110 | fflush(target_file_); 111 | } 112 | 113 | void flush() override 114 | { 115 | std::lock_guard lock(mutex_); 116 | fflush(target_file_); 117 | } 118 | 119 | void set_pattern(const std::string &pattern) SPDLOG_FINAL 120 | { 121 | std::lock_guard lock(mutex_); 122 | formatter_ = std::unique_ptr(new pattern_formatter(pattern)); 123 | } 124 | 125 | void set_formatter(std::unique_ptr sink_formatter) override 126 | { 127 | std::lock_guard lock(mutex_); 128 | formatter_ = std::move(sink_formatter); 129 | } 130 | 131 | private: 132 | void print_ccode_(const std::string &color_code) 133 | { 134 | fwrite(color_code.data(), sizeof(char), color_code.size(), target_file_); 135 | } 136 | void print_range_(const fmt::memory_buffer &formatted, size_t start, size_t end) 137 | { 138 | fwrite(formatted.data() + start, sizeof(char), end - start, target_file_); 139 | } 140 | 141 | FILE *target_file_; 142 | mutex_t &mutex_; 143 | 144 | bool should_do_colors_; 145 | std::unordered_map colors_; 146 | }; 147 | 148 | using ansicolor_stdout_sink_mt = ansicolor_sink; 149 | using ansicolor_stdout_sink_st = ansicolor_sink; 150 | 151 | using ansicolor_stderr_sink_mt = ansicolor_sink; 152 | using ansicolor_stderr_sink_st = ansicolor_sink; 153 | 154 | } // namespace sinks 155 | 156 | } // namespace spdlog 157 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/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 override the sink_it_() and flush_() methods. 10 | // locking is taken care of in this class - no locking needed by the 11 | // implementers.. 12 | // 13 | 14 | #include "spdlog/common.h" 15 | #include "spdlog/details/log_msg.h" 16 | #include "spdlog/formatter.h" 17 | #include "spdlog/sinks/sink.h" 18 | 19 | namespace spdlog { 20 | namespace sinks { 21 | template 22 | class base_sink : public sink 23 | { 24 | public: 25 | base_sink() 26 | : sink() 27 | { 28 | } 29 | 30 | base_sink(const base_sink &) = delete; 31 | base_sink &operator=(const base_sink &) = delete; 32 | 33 | void log(const details::log_msg &msg) SPDLOG_FINAL 34 | { 35 | std::lock_guard lock(mutex_); 36 | sink_it_(msg); 37 | } 38 | 39 | void flush() SPDLOG_FINAL override 40 | { 41 | std::lock_guard lock(mutex_); 42 | flush_(); 43 | } 44 | 45 | void set_pattern(const std::string &pattern) SPDLOG_FINAL override 46 | { 47 | std::lock_guard lock(mutex_); 48 | formatter_ = std::unique_ptr(new pattern_formatter(pattern)); 49 | } 50 | 51 | void set_formatter(std::unique_ptr sink_formatter) SPDLOG_FINAL override 52 | { 53 | std::lock_guard lock(mutex_); 54 | formatter_ = std::move(sink_formatter); 55 | } 56 | 57 | protected: 58 | virtual void sink_it_(const details::log_msg &msg) = 0; 59 | virtual void flush_() = 0; 60 | Mutex mutex_; 61 | }; 62 | } // namespace sinks 63 | } // namespace spdlog 64 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/sinks/basic_file_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015-2018 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | #include "spdlog/details/file_helper.h" 8 | #include "spdlog/details/null_mutex.h" 9 | #include "spdlog/sinks/base_sink.h" 10 | #include "spdlog/spdlog.h" 11 | 12 | #include 13 | #include 14 | 15 | namespace spdlog { 16 | namespace sinks { 17 | /* 18 | * Trivial file sink with single file as target 19 | */ 20 | template 21 | class basic_file_sink SPDLOG_FINAL : public base_sink 22 | { 23 | public: 24 | explicit basic_file_sink(const filename_t &filename, bool truncate = false) 25 | { 26 | file_helper_.open(filename, truncate); 27 | } 28 | 29 | protected: 30 | void sink_it_(const details::log_msg &msg) override 31 | { 32 | fmt::memory_buffer formatted; 33 | sink::formatter_->format(msg, formatted); 34 | file_helper_.write(formatted); 35 | } 36 | 37 | void flush_() override 38 | { 39 | file_helper_.flush(); 40 | } 41 | 42 | private: 43 | details::file_helper file_helper_; 44 | }; 45 | 46 | using basic_file_sink_mt = basic_file_sink; 47 | using basic_file_sink_st = basic_file_sink; 48 | 49 | } // namespace sinks 50 | 51 | // 52 | // factory functions 53 | // 54 | template 55 | inline std::shared_ptr basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false) 56 | { 57 | return Factory::template create(logger_name, filename, truncate); 58 | } 59 | 60 | template 61 | inline std::shared_ptr basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false) 62 | { 63 | return Factory::template create(logger_name, filename, truncate); 64 | } 65 | 66 | } // namespace spdlog 67 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/sinks/daily_file_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 | #include "spdlog/details/file_helper.h" 8 | #include "spdlog/details/null_mutex.h" 9 | #include "spdlog/fmt/fmt.h" 10 | #include "spdlog/sinks/base_sink.h" 11 | #include "spdlog/spdlog.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace spdlog { 20 | namespace sinks { 21 | 22 | /* 23 | * Generator of daily log file names in format basename.YYYY-MM-DD.ext 24 | */ 25 | struct daily_filename_calculator 26 | { 27 | // Create filename for the form basename.YYYY-MM-DD 28 | static filename_t calc_filename(const filename_t &filename, const tm &now_tm) 29 | { 30 | filename_t basename, ext; 31 | std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename); 32 | std::conditional::value, fmt::memory_buffer, fmt::wmemory_buffer>::type w; 33 | fmt::format_to( 34 | w, SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday, ext); 35 | return fmt::to_string(w); 36 | } 37 | }; 38 | 39 | /* 40 | * Rotating file sink based on date. rotates at midnight 41 | */ 42 | template 43 | class daily_file_sink SPDLOG_FINAL : public base_sink 44 | { 45 | public: 46 | // create daily file sink which rotates on given time 47 | daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate = false) 48 | : base_filename_(std::move(base_filename)) 49 | , rotation_h_(rotation_hour) 50 | , rotation_m_(rotation_minute) 51 | , truncate_(truncate) 52 | { 53 | if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59) 54 | { 55 | throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor"); 56 | } 57 | auto now = log_clock::now(); 58 | file_helper_.open(FileNameCalc::calc_filename(base_filename_, now_tm(now)), truncate_); 59 | rotation_tp_ = next_rotation_tp_(); 60 | } 61 | 62 | protected: 63 | void sink_it_(const details::log_msg &msg) override 64 | { 65 | 66 | if (msg.time >= rotation_tp_) 67 | { 68 | file_helper_.open(FileNameCalc::calc_filename(base_filename_, now_tm(msg.time)), truncate_); 69 | rotation_tp_ = next_rotation_tp_(); 70 | } 71 | fmt::memory_buffer formatted; 72 | sink::formatter_->format(msg, formatted); 73 | file_helper_.write(formatted); 74 | } 75 | 76 | void flush_() override 77 | { 78 | file_helper_.flush(); 79 | } 80 | 81 | private: 82 | tm now_tm(log_clock::time_point tp) 83 | { 84 | time_t tnow = log_clock::to_time_t(tp); 85 | return spdlog::details::os::localtime(tnow); 86 | } 87 | 88 | log_clock::time_point next_rotation_tp_() 89 | { 90 | auto now = log_clock::now(); 91 | tm date = now_tm(now); 92 | date.tm_hour = rotation_h_; 93 | date.tm_min = rotation_m_; 94 | date.tm_sec = 0; 95 | auto rotation_time = log_clock::from_time_t(std::mktime(&date)); 96 | if (rotation_time > now) 97 | { 98 | return rotation_time; 99 | } 100 | return {rotation_time + std::chrono::hours(24)}; 101 | } 102 | 103 | filename_t base_filename_; 104 | int rotation_h_; 105 | int rotation_m_; 106 | log_clock::time_point rotation_tp_; 107 | details::file_helper file_helper_; 108 | bool truncate_; 109 | }; 110 | 111 | using daily_file_sink_mt = daily_file_sink; 112 | using daily_file_sink_st = daily_file_sink; 113 | 114 | } // namespace sinks 115 | 116 | // 117 | // factory functions 118 | // 119 | template 120 | inline std::shared_ptr daily_logger_mt( 121 | const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false) 122 | { 123 | return Factory::template create(logger_name, filename, hour, minute, truncate); 124 | } 125 | 126 | template 127 | inline std::shared_ptr daily_logger_st( 128 | const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false) 129 | { 130 | return Factory::template create(logger_name, filename, hour, minute, truncate); 131 | } 132 | } // namespace spdlog 133 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/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 "base_sink.h" 9 | #include "spdlog/details/log_msg.h" 10 | #include "spdlog/details/null_mutex.h" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | // Distribution sink (mux). Stores a vector of sinks which get called when log 18 | // is called 19 | 20 | namespace spdlog { 21 | namespace sinks { 22 | 23 | template 24 | class dist_sink : public base_sink 25 | { 26 | public: 27 | dist_sink() = default; 28 | dist_sink(const dist_sink &) = delete; 29 | dist_sink &operator=(const dist_sink &) = delete; 30 | 31 | void add_sink(std::shared_ptr sink) 32 | { 33 | std::lock_guard lock(base_sink::mutex_); 34 | sinks_.push_back(sink); 35 | } 36 | 37 | void remove_sink(std::shared_ptr sink) 38 | { 39 | std::lock_guard lock(base_sink::mutex_); 40 | sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end()); 41 | } 42 | 43 | void set_sinks(std::vector> sinks) 44 | { 45 | std::lock_guard lock(base_sink::mutex_); 46 | sinks_ = std::move(sinks); 47 | } 48 | 49 | protected: 50 | void sink_it_(const details::log_msg &msg) override 51 | { 52 | 53 | for (auto &sink : sinks_) 54 | { 55 | if (sink->should_log(msg.level)) 56 | { 57 | sink->log(msg); 58 | } 59 | } 60 | } 61 | 62 | void flush_() override 63 | { 64 | for (auto &sink : sinks_) 65 | sink->flush(); 66 | } 67 | std::vector> sinks_; 68 | }; 69 | 70 | using dist_sink_mt = dist_sink; 71 | using dist_sink_st = dist_sink; 72 | 73 | } // namespace sinks 74 | } // namespace spdlog 75 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/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 "spdlog/details/null_mutex.h" 11 | #include "spdlog/sinks/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 | 33 | fmt::memory_buffer formatted; 34 | sink::formatter_->format(msg, formatted); 35 | OutputDebugStringA(fmt::to_string(formatted).c_str()); 36 | } 37 | 38 | void flush_() override {} 39 | }; 40 | 41 | using msvc_sink_mt = msvc_sink; 42 | using msvc_sink_st = msvc_sink; 43 | 44 | using windebug_sink_mt = msvc_sink_mt; 45 | using windebug_sink_st = msvc_sink_st; 46 | 47 | } // namespace sinks 48 | } // namespace spdlog 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/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 "spdlog/details/null_mutex.h" 9 | #include "spdlog/sinks/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 | void flush_() override {} 22 | }; 23 | 24 | using null_sink_mt = null_sink; 25 | using null_sink_st = null_sink; 26 | 27 | } // namespace sinks 28 | } // namespace spdlog 29 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/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 "spdlog/details/null_mutex.h" 9 | #include "spdlog/sinks/base_sink.h" 10 | 11 | #include 12 | #include 13 | 14 | namespace spdlog { 15 | namespace sinks { 16 | template 17 | class ostream_sink SPDLOG_FINAL : 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 | fmt::memory_buffer formatted; 32 | sink::formatter_->format(msg, formatted); 33 | ostream_.write(formatted.data(), static_cast(formatted.size())); 34 | if (force_flush_) 35 | ostream_.flush(); 36 | } 37 | 38 | void flush_() override 39 | { 40 | ostream_.flush(); 41 | } 42 | 43 | std::ostream &ostream_; 44 | bool force_flush_; 45 | }; 46 | 47 | using ostream_sink_mt = ostream_sink; 48 | using ostream_sink_st = ostream_sink; 49 | 50 | } // namespace sinks 51 | } // namespace spdlog 52 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/sinks/rotating_file_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 | #include "spdlog/details/file_helper.h" 8 | #include "spdlog/details/null_mutex.h" 9 | #include "spdlog/fmt/fmt.h" 10 | #include "spdlog/sinks/base_sink.h" 11 | #include "spdlog/spdlog.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace spdlog { 21 | namespace sinks { 22 | 23 | // 24 | // Rotating file sink based on size 25 | // 26 | template 27 | class rotating_file_sink SPDLOG_FINAL : public base_sink 28 | { 29 | public: 30 | rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files) 31 | : base_filename_(std::move(base_filename)) 32 | , max_size_(max_size) 33 | , max_files_(max_files) 34 | { 35 | file_helper_.open(calc_filename(base_filename_, 0)); 36 | current_size_ = file_helper_.size(); // expensive. called only once 37 | } 38 | 39 | // calc filename according to index and file extension if exists. 40 | // e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt". 41 | static filename_t calc_filename(const filename_t &filename, std::size_t index) 42 | { 43 | typename std::conditional::value, fmt::memory_buffer, fmt::wmemory_buffer>::type w; 44 | if (index != 0u) 45 | { 46 | filename_t basename, ext; 47 | std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename); 48 | fmt::format_to(w, SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext); 49 | } 50 | else 51 | { 52 | fmt::format_to(w, SPDLOG_FILENAME_T("{}"), filename); 53 | } 54 | return fmt::to_string(w); 55 | } 56 | 57 | protected: 58 | void sink_it_(const details::log_msg &msg) override 59 | { 60 | fmt::memory_buffer formatted; 61 | sink::formatter_->format(msg, formatted); 62 | current_size_ += formatted.size(); 63 | if (current_size_ > max_size_) 64 | { 65 | rotate_(); 66 | current_size_ = formatted.size(); 67 | } 68 | file_helper_.write(formatted); 69 | } 70 | 71 | void flush_() override 72 | { 73 | file_helper_.flush(); 74 | } 75 | 76 | private: 77 | // Rotate files: 78 | // log.txt -> log.1.txt 79 | // log.1.txt -> log.2.txt 80 | // log.2.txt -> log.3.txt 81 | // log.3.txt -> delete 82 | void rotate_() 83 | { 84 | using details::os::filename_to_str; 85 | file_helper_.close(); 86 | for (auto i = max_files_; i > 0; --i) 87 | { 88 | filename_t src = calc_filename(base_filename_, i - 1); 89 | filename_t target = calc_filename(base_filename_, i); 90 | 91 | if (details::file_helper::file_exists(target)) 92 | { 93 | if (details::os::remove(target) != 0) 94 | { 95 | throw spdlog_ex("rotating_file_sink: failed removing " + filename_to_str(target), errno); 96 | } 97 | } 98 | if (details::file_helper::file_exists(src) && details::os::rename(src, target) != 0) 99 | { 100 | // if failed try again after small delay. 101 | // this is a workaround to a windows issue, where very high rotation 102 | // rates sometimes fail (because of antivirus?). 103 | details::os::sleep_for_millis(20); 104 | details::os::remove(target); 105 | if (details::os::rename(src, target) != 0) 106 | { 107 | throw spdlog_ex( 108 | "rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno); 109 | } 110 | } 111 | } 112 | file_helper_.reopen(true); 113 | } 114 | 115 | filename_t base_filename_; 116 | std::size_t max_size_; 117 | std::size_t max_files_; 118 | std::size_t current_size_; 119 | details::file_helper file_helper_; 120 | }; 121 | 122 | using rotating_file_sink_mt = rotating_file_sink; 123 | using rotating_file_sink_st = rotating_file_sink; 124 | 125 | } // namespace sinks 126 | 127 | // 128 | // factory functions 129 | // 130 | 131 | template 132 | inline std::shared_ptr rotating_logger_mt( 133 | const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files) 134 | { 135 | return Factory::template create(logger_name, filename, max_file_size, max_files); 136 | } 137 | 138 | template 139 | inline std::shared_ptr rotating_logger_st( 140 | const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files) 141 | { 142 | return Factory::template create(logger_name, filename, max_file_size, max_files); 143 | } 144 | } // namespace spdlog 145 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/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 "spdlog/details/log_msg.h" 9 | #include "spdlog/details/pattern_formatter.h" 10 | #include "spdlog/formatter.h" 11 | 12 | namespace spdlog { 13 | namespace sinks { 14 | class sink 15 | { 16 | public: 17 | sink() 18 | : level_(level::trace) 19 | , formatter_(new pattern_formatter("%+")) 20 | { 21 | } 22 | 23 | explicit sink(std::unique_ptr formatter) 24 | : level_(level::trace) 25 | , formatter_(std::move(formatter)) 26 | { 27 | } 28 | 29 | virtual ~sink() = default; 30 | virtual void log(const details::log_msg &msg) = 0; 31 | virtual void flush() = 0; 32 | virtual void set_pattern(const std::string &pattern) = 0; 33 | virtual void set_formatter(std::unique_ptr sink_formatter) = 0; 34 | 35 | bool should_log(level::level_enum msg_level) const 36 | { 37 | return msg_level >= level_.load(std::memory_order_relaxed); 38 | } 39 | 40 | void set_level(level::level_enum log_level) 41 | { 42 | level_.store(log_level); 43 | } 44 | 45 | level::level_enum level() const 46 | { 47 | return static_cast(level_.load(std::memory_order_relaxed)); 48 | } 49 | 50 | protected: 51 | // sink log level - default is all 52 | level_t level_; 53 | 54 | // sink formatter - default is full format 55 | std::unique_ptr formatter_; 56 | }; 57 | 58 | } // namespace sinks 59 | } // namespace spdlog 60 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/sinks/stdout_color_sinks.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2018 spdlog 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #include "spdlog/spdlog.h" 9 | #ifdef _WIN32 10 | #include "spdlog/sinks/wincolor_sink.h" 11 | #else 12 | #include "spdlog/sinks/ansicolor_sink.h" 13 | #endif 14 | 15 | namespace spdlog { 16 | namespace sinks { 17 | #ifdef _WIN32 18 | using stdout_color_sink_mt = wincolor_stdout_sink_mt; 19 | using stdout_color_sink_st = wincolor_stdout_sink_st; 20 | using stderr_color_sink_mt = wincolor_stderr_sink_mt; 21 | using stderr_color_sink_st = wincolor_stderr_sink_st; 22 | #else 23 | using stdout_color_sink_mt = ansicolor_stdout_sink_mt; 24 | using stdout_color_sink_st = ansicolor_stdout_sink_st; 25 | using stderr_color_sink_mt = ansicolor_stderr_sink_mt; 26 | using stderr_color_sink_st = ansicolor_stderr_sink_st; 27 | #endif 28 | } // namespace sinks 29 | 30 | template 31 | inline std::shared_ptr stdout_color_mt(const std::string &logger_name) 32 | { 33 | return Factory::template create(logger_name); 34 | } 35 | 36 | template 37 | inline std::shared_ptr stdout_color_st(const std::string &logger_name) 38 | { 39 | return Factory::template create(logger_name); 40 | } 41 | 42 | template 43 | inline std::shared_ptr stderr_color_mt(const std::string &logger_name) 44 | { 45 | return Factory::template create(logger_name); 46 | } 47 | 48 | template 49 | inline std::shared_ptr stderr_color_st(const std::string &logger_name) 50 | { 51 | return Factory::template create(logger_name); 52 | } 53 | } // namespace spdlog 54 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/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 "spdlog/details/console_globals.h" 9 | #include "spdlog/details/null_mutex.h" 10 | #include "spdlog/spdlog.h" 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | namespace spdlog { 17 | 18 | namespace sinks { 19 | 20 | template 21 | class stdout_sink SPDLOG_FINAL : public sink 22 | { 23 | public: 24 | using mutex_t = typename ConsoleMutex::mutex_t; 25 | stdout_sink() 26 | : mutex_(ConsoleMutex::mutex()) 27 | , file_(TargetStream::stream()) 28 | { 29 | } 30 | ~stdout_sink() override = default; 31 | 32 | stdout_sink(const stdout_sink &other) = delete; 33 | stdout_sink &operator=(const stdout_sink &other) = delete; 34 | 35 | void log(const details::log_msg &msg) override 36 | { 37 | std::lock_guard lock(mutex_); 38 | fmt::memory_buffer formatted; 39 | formatter_->format(msg, formatted); 40 | fwrite(formatted.data(), sizeof(char), formatted.size(), file_); 41 | fflush(TargetStream::stream()); 42 | } 43 | 44 | void flush() override 45 | { 46 | std::lock_guard lock(mutex_); 47 | fflush(file_); 48 | } 49 | 50 | void set_pattern(const std::string &pattern) override 51 | { 52 | std::lock_guard lock(mutex_); 53 | formatter_ = std::unique_ptr(new pattern_formatter(pattern)); 54 | } 55 | 56 | void set_formatter(std::unique_ptr sink_formatter) override 57 | { 58 | std::lock_guard lock(mutex_); 59 | formatter_ = std::move(sink_formatter); 60 | } 61 | 62 | private: 63 | mutex_t &mutex_; 64 | FILE *file_; 65 | }; 66 | 67 | using stdout_sink_mt = stdout_sink; 68 | using stdout_sink_st = stdout_sink; 69 | 70 | using stderr_sink_mt = stdout_sink; 71 | using stderr_sink_st = stdout_sink; 72 | 73 | } // namespace sinks 74 | 75 | // factory methods 76 | template 77 | inline std::shared_ptr stdout_logger_mt(const std::string &logger_name) 78 | { 79 | return Factory::template create(logger_name); 80 | } 81 | 82 | template 83 | inline std::shared_ptr stdout_logger_st(const std::string &logger_name) 84 | { 85 | return Factory::template create(logger_name); 86 | } 87 | 88 | template 89 | inline std::shared_ptr stderr_logger_mt(const std::string &logger_name) 90 | { 91 | return Factory::template create(logger_name); 92 | } 93 | 94 | template 95 | inline std::shared_ptr stderr_logger_st(const std::string &logger_name) 96 | { 97 | return Factory::template create(logger_name); 98 | } 99 | } // namespace spdlog 100 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/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 "spdlog/sinks/base_sink.h" 9 | #include "spdlog/spdlog.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace spdlog { 16 | namespace sinks { 17 | /** 18 | * Sink that write to syslog using the `syscall()` library call. 19 | * 20 | * Locking is not needed, as `syslog()` itself is thread-safe. 21 | */ 22 | template 23 | class syslog_sink : public base_sink 24 | { 25 | public: 26 | // 27 | explicit syslog_sink(std::string ident = "", int syslog_option = 0, int syslog_facility = LOG_USER) 28 | : ident_(std::move(ident)) 29 | { 30 | priorities_[static_cast(level::trace)] = LOG_DEBUG; 31 | priorities_[static_cast(level::debug)] = LOG_DEBUG; 32 | priorities_[static_cast(level::info)] = LOG_INFO; 33 | priorities_[static_cast(level::warn)] = LOG_WARNING; 34 | priorities_[static_cast(level::err)] = LOG_ERR; 35 | priorities_[static_cast(level::critical)] = LOG_CRIT; 36 | priorities_[static_cast(level::off)] = LOG_INFO; 37 | 38 | // set ident to be program name if empty 39 | ::openlog(ident_.empty() ? nullptr : ident_.c_str(), syslog_option, syslog_facility); 40 | } 41 | 42 | ~syslog_sink() override 43 | { 44 | ::closelog(); 45 | } 46 | 47 | syslog_sink(const syslog_sink &) = delete; 48 | syslog_sink &operator=(const syslog_sink &) = delete; 49 | 50 | protected: 51 | void sink_it_(const details::log_msg &msg) override 52 | { 53 | ::syslog(syslog_prio_from_level(msg), "%s", fmt::to_string(msg.raw).c_str()); 54 | } 55 | 56 | void flush_() override {} 57 | 58 | private: 59 | std::array priorities_; 60 | // must store the ident because the man says openlog might use the pointer as 61 | // is and not a string copy 62 | const std::string ident_; 63 | 64 | // 65 | // Simply maps spdlog's log level to syslog priority level. 66 | // 67 | int syslog_prio_from_level(const details::log_msg &msg) const 68 | { 69 | return priorities_[static_cast(msg.level)]; 70 | } 71 | }; 72 | 73 | using syslog_sink_mt = syslog_sink; 74 | using syslog_sink_st = syslog_sink; 75 | } // namespace sinks 76 | 77 | // Create and register a syslog logger 78 | template 79 | inline std::shared_ptr syslog_logger_mt( 80 | const std::string &logger_name, const std::string &syslog_ident = "", int syslog_option = 0, int syslog_facility = (1 << 3)) 81 | { 82 | return Factory::template create(logger_name, syslog_ident, syslog_option, syslog_facility); 83 | } 84 | 85 | template 86 | inline std::shared_ptr syslog_logger_st( 87 | const std::string &logger_name, const std::string &syslog_ident = "", int syslog_option = 0, int syslog_facility = (1 << 3)) 88 | { 89 | return Factory::template create(logger_name, syslog_ident, syslog_option, syslog_facility); 90 | } 91 | } // namespace spdlog 92 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/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 "spdlog/common.h" 9 | #include "spdlog/details/console_globals.h" 10 | #include "spdlog/details/null_mutex.h" 11 | #include "spdlog/sinks/sink.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace spdlog { 20 | namespace sinks { 21 | /* 22 | * Windows color console sink. Uses WriteConsoleA to write to the console with 23 | * colors 24 | */ 25 | template 26 | class wincolor_sink : public sink 27 | { 28 | public: 29 | const WORD BOLD = FOREGROUND_INTENSITY; 30 | const WORD RED = FOREGROUND_RED; 31 | const WORD GREEN = FOREGROUND_GREEN; 32 | const WORD CYAN = FOREGROUND_GREEN | FOREGROUND_BLUE; 33 | const WORD WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; 34 | const WORD YELLOW = FOREGROUND_RED | FOREGROUND_GREEN; 35 | 36 | wincolor_sink() 37 | : out_handle_(OutHandle::handle()) 38 | , mutex_(ConsoleMutex::mutex()) 39 | { 40 | colors_[level::trace] = WHITE; 41 | colors_[level::debug] = CYAN; 42 | colors_[level::info] = GREEN; 43 | colors_[level::warn] = YELLOW | BOLD; 44 | colors_[level::err] = RED | BOLD; // red bold 45 | colors_[level::critical] = BACKGROUND_RED | WHITE | BOLD; // white bold on red background 46 | colors_[level::off] = 0; 47 | } 48 | 49 | ~wincolor_sink() override 50 | { 51 | this->flush(); 52 | } 53 | 54 | wincolor_sink(const wincolor_sink &other) = delete; 55 | wincolor_sink &operator=(const wincolor_sink &other) = delete; 56 | 57 | // change the color for the given level 58 | void set_color(level::level_enum level, WORD color) 59 | { 60 | std::lock_guard lock(mutex_); 61 | colors_[level] = color; 62 | } 63 | 64 | void log(const details::log_msg &msg) SPDLOG_FINAL override 65 | { 66 | std::lock_guard lock(mutex_); 67 | fmt::memory_buffer formatted; 68 | formatter_->format(msg, formatted); 69 | if (msg.color_range_end > msg.color_range_start) 70 | { 71 | // before color range 72 | print_range_(formatted, 0, msg.color_range_start); 73 | 74 | // in color range 75 | auto orig_attribs = set_console_attribs(colors_[msg.level]); 76 | print_range_(formatted, msg.color_range_start, msg.color_range_end); 77 | ::SetConsoleTextAttribute(out_handle_, 78 | orig_attribs); // reset to orig colors 79 | // after color range 80 | print_range_(formatted, msg.color_range_end, formatted.size()); 81 | } 82 | else // print without colors if color range is invalid 83 | { 84 | print_range_(formatted, 0, formatted.size()); 85 | } 86 | } 87 | 88 | void flush() SPDLOG_FINAL override 89 | { 90 | // windows console always flushed? 91 | } 92 | 93 | void set_pattern(const std::string &pattern) override SPDLOG_FINAL 94 | { 95 | std::lock_guard lock(mutex_); 96 | formatter_ = std::unique_ptr(new pattern_formatter(pattern)); 97 | } 98 | 99 | void set_formatter(std::unique_ptr sink_formatter) override SPDLOG_FINAL 100 | { 101 | std::lock_guard lock(mutex_); 102 | formatter_ = std::move(sink_formatter); 103 | } 104 | 105 | private: 106 | using mutex_t = typename ConsoleMutex::mutex_t; 107 | // set color and return the orig console attributes (for resetting later) 108 | WORD set_console_attribs(WORD attribs) 109 | { 110 | CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info; 111 | ::GetConsoleScreenBufferInfo(out_handle_, &orig_buffer_info); 112 | WORD back_color = orig_buffer_info.wAttributes; 113 | // retrieve the current background color 114 | back_color &= static_cast(~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY)); 115 | // keep the background color unchanged 116 | ::SetConsoleTextAttribute(out_handle_, attribs | back_color); 117 | return orig_buffer_info.wAttributes; // return orig attribs 118 | } 119 | 120 | // print a range of formatted message to console 121 | void print_range_(const fmt::memory_buffer &formatted, size_t start, size_t end) 122 | { 123 | auto size = static_cast(end - start); 124 | ::WriteConsoleA(out_handle_, formatted.data() + start, size, nullptr, nullptr); 125 | } 126 | 127 | HANDLE out_handle_; 128 | mutex_t &mutex_; 129 | std::unordered_map colors_; 130 | }; 131 | 132 | using wincolor_stdout_sink_mt = wincolor_sink; 133 | using wincolor_stdout_sink_st = wincolor_sink; 134 | 135 | using wincolor_stderr_sink_mt = wincolor_sink; 136 | using wincolor_stderr_sink_st = wincolor_sink; 137 | 138 | } // namespace sinks 139 | } // namespace spdlog 140 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/spdlog.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015-2018 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 "spdlog/common.h" 11 | #include "spdlog/details/registry.h" 12 | #include "spdlog/logger.h" 13 | #include "spdlog/version.h" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace spdlog { 21 | 22 | // Default logger factory- creates synchronous loggers 23 | struct synchronous_factory 24 | { 25 | template 26 | 27 | static std::shared_ptr create(std::string logger_name, SinkArgs &&... args) 28 | { 29 | auto sink = std::make_shared(std::forward(args)...); 30 | auto new_logger = std::make_shared(std::move(logger_name), std::move(sink)); 31 | details::registry::instance().register_and_init(new_logger); 32 | return new_logger; 33 | } 34 | }; 35 | 36 | using default_factory = synchronous_factory; 37 | 38 | // Create and register a logger with a templated sink type 39 | // The logger's level, formatter and flush level will be set according the 40 | // global settings. 41 | // Example: 42 | // spdlog::create("logger_name", "dailylog_filename", 11, 59); 43 | template 44 | inline std::shared_ptr create(std::string logger_name, SinkArgs &&... sink_args) 45 | { 46 | return default_factory::create(std::move(logger_name), std::forward(sink_args)...); 47 | } 48 | 49 | // Return an existing logger or nullptr if a logger with such name doesn't 50 | // exist. 51 | // example: spdlog::get("my_logger")->info("hello {}", "world"); 52 | inline std::shared_ptr get(const std::string &name) 53 | { 54 | return details::registry::instance().get(name); 55 | } 56 | 57 | // Set global formatter. Each sink in each logger will get a clone of this object 58 | inline void set_formatter(std::unique_ptr formatter) 59 | { 60 | details::registry::instance().set_formatter(std::move(formatter)); 61 | } 62 | 63 | // Set global format string. 64 | // example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v"); 65 | inline void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local) 66 | { 67 | set_formatter(std::unique_ptr(new pattern_formatter(std::move(pattern), time_type))); 68 | } 69 | 70 | // Set global logging level 71 | inline void set_level(level::level_enum log_level) 72 | { 73 | details::registry::instance().set_level(log_level); 74 | } 75 | 76 | // Set global flush level 77 | inline void flush_on(level::level_enum log_level) 78 | { 79 | details::registry::instance().flush_on(log_level); 80 | } 81 | 82 | // Start/Restart a periodic flusher thread 83 | // Warning: Use only if all your loggers are thread safe! 84 | inline void flush_every(std::chrono::seconds interval) 85 | { 86 | details::registry::instance().flush_every(interval); 87 | } 88 | 89 | // Set global error handler 90 | inline void set_error_handler(log_err_handler handler) 91 | { 92 | details::registry::instance().set_error_handler(std::move(handler)); 93 | } 94 | 95 | // Register the given logger with the given name 96 | inline void register_logger(std::shared_ptr logger) 97 | { 98 | details::registry::instance().register_logger(std::move(logger)); 99 | } 100 | 101 | // Apply a user defined function on all registered loggers 102 | // Example: 103 | // spdlog::apply_all([&](std::shared_ptr l) {l->flush();}); 104 | inline void apply_all(const std::function)> &fun) 105 | { 106 | details::registry::instance().apply_all(fun); 107 | } 108 | 109 | // Drop the reference to the given logger 110 | inline void drop(const std::string &name) 111 | { 112 | details::registry::instance().drop(name); 113 | } 114 | 115 | // Drop all references from the registry 116 | inline void drop_all() 117 | { 118 | details::registry::instance().drop_all(); 119 | } 120 | 121 | // stop any running threads started by spdlog and clean registry loggers 122 | inline void shutdown() 123 | { 124 | details::registry::instance().shutdown(); 125 | } 126 | 127 | /////////////////////////////////////////////////////////////////////////////// 128 | // 129 | // Trace & Debug can be switched on/off at compile time for zero cost debug 130 | // statements. 131 | // Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in tweakme.h to enable. 132 | // SPDLOG_TRACE(..) will also print current file and line. 133 | // 134 | // Example: 135 | // spdlog::set_level(spdlog::level::trace); 136 | // SPDLOG_TRACE(my_logger, "some trace message"); 137 | // SPDLOG_TRACE(my_logger, "another trace message {} {}", 1, 2); 138 | // SPDLOG_DEBUG(my_logger, "some debug message {} {}", 3, 4); 139 | /////////////////////////////////////////////////////////////////////////////// 140 | 141 | #ifdef SPDLOG_TRACE_ON 142 | #define SPDLOG_STR_H(x) #x 143 | #define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x) 144 | #ifdef _MSC_VER 145 | #define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ "(" SPDLOG_STR_HELPER(__LINE__) ") ] " __VA_ARGS__) 146 | #else 147 | #define SPDLOG_TRACE(logger, ...) \ 148 | logger->trace("[ " __FILE__ ":" SPDLOG_STR_HELPER(__LINE__) " ]" \ 149 | " " __VA_ARGS__) 150 | #endif 151 | #else 152 | #define SPDLOG_TRACE(logger, ...) (void)0 153 | #endif 154 | 155 | #ifdef SPDLOG_DEBUG_ON 156 | #define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__) 157 | #else 158 | #define SPDLOG_DEBUG(logger, ...) (void)0 159 | #endif 160 | 161 | } // namespace spdlog 162 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/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 11 | // features 12 | // 13 | /////////////////////////////////////////////////////////////////////////////// 14 | 15 | /////////////////////////////////////////////////////////////////////////////// 16 | // Under Linux, the much faster CLOCK_REALTIME_COARSE clock can be used. 17 | // This clock is less accurate - can be off by dozens of millis - depending on 18 | // the kernel HZ. 19 | // Uncomment to use it instead of the regular clock. 20 | // 21 | // #define SPDLOG_CLOCK_COARSE 22 | /////////////////////////////////////////////////////////////////////////////// 23 | 24 | /////////////////////////////////////////////////////////////////////////////// 25 | // Uncomment if date/time logging is not needed and never appear in the log 26 | // pattern. 27 | // This will prevent spdlog from querying the clock on each log call. 28 | // 29 | // WARNING: If the log pattern contains any date/time while this flag is on, the 30 | // result is undefined. 31 | // You must set new pattern(spdlog::set_pattern(..") without any 32 | // date/time in it 33 | // 34 | // #define SPDLOG_NO_DATETIME 35 | /////////////////////////////////////////////////////////////////////////////// 36 | 37 | /////////////////////////////////////////////////////////////////////////////// 38 | // Uncomment if thread id logging is not needed (i.e. no %t in the log pattern). 39 | // This will prevent spdlog from querying the thread id on each log call. 40 | // 41 | // WARNING: If the log pattern contains thread id (i.e, %t) while this flag is 42 | // on, the result is undefined. 43 | // 44 | // #define SPDLOG_NO_THREAD_ID 45 | /////////////////////////////////////////////////////////////////////////////// 46 | 47 | /////////////////////////////////////////////////////////////////////////////// 48 | // Uncomment to prevent spdlog from caching thread ids in thread local storage. 49 | // By default spdlog saves thread ids in tls to gain a few micros for each call. 50 | // 51 | // WARNING: if your program forks, UNCOMMENT this flag to prevent undefined 52 | // thread ids in the children logs. 53 | // 54 | // #define SPDLOG_DISABLE_TID_CACHING 55 | /////////////////////////////////////////////////////////////////////////////// 56 | 57 | /////////////////////////////////////////////////////////////////////////////// 58 | // Uncomment if logger name logging is not needed. 59 | // This will prevent spdlog from copying the logger name on each log call. 60 | // 61 | // #define SPDLOG_NO_NAME 62 | /////////////////////////////////////////////////////////////////////////////// 63 | 64 | /////////////////////////////////////////////////////////////////////////////// 65 | // Uncomment to enable the SPDLOG_DEBUG/SPDLOG_TRACE macros. 66 | // 67 | // #define SPDLOG_DEBUG_ON 68 | // #define SPDLOG_TRACE_ON 69 | /////////////////////////////////////////////////////////////////////////////// 70 | 71 | /////////////////////////////////////////////////////////////////////////////// 72 | // Uncomment to avoid spdlog's usage of atomic log levels 73 | // Use only if your code never modifies a logger's log levels concurrently by 74 | // 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 94 | // accordingly. 95 | // 96 | // #define SPDLOG_FMT_EXTERNAL 97 | /////////////////////////////////////////////////////////////////////////////// 98 | 99 | /////////////////////////////////////////////////////////////////////////////// 100 | // Uncomment to enable wchar_t support (convert to utf8) 101 | // 102 | // #define SPDLOG_WCHAR_TO_UTF8_SUPPORT 103 | /////////////////////////////////////////////////////////////////////////////// 104 | 105 | /////////////////////////////////////////////////////////////////////////////// 106 | // Uncomment to prevent child processes from inheriting log file descriptors 107 | // 108 | // #define SPDLOG_PREVENT_CHILD_FD 109 | /////////////////////////////////////////////////////////////////////////////// 110 | 111 | /////////////////////////////////////////////////////////////////////////////// 112 | // Uncomment if your compiler doesn't support the "final" keyword. 113 | // The final keyword allows more optimizations in release 114 | // mode with recent compilers. See GCC's documentation for -Wsuggest-final-types 115 | // for instance. 116 | // 117 | // #define SPDLOG_NO_FINAL 118 | /////////////////////////////////////////////////////////////////////////////// 119 | 120 | /////////////////////////////////////////////////////////////////////////////// 121 | // Uncomment to enable message counting feature. 122 | // Use the %i in the logger pattern to display log message sequence id. 123 | // 124 | // #define SPDLOG_ENABLE_MESSAGE_COUNTER 125 | /////////////////////////////////////////////////////////////////////////////// 126 | 127 | /////////////////////////////////////////////////////////////////////////////// 128 | // Uncomment to customize level names (e.g. "MT TRACE") 129 | // 130 | // #define SPDLOG_LEVEL_NAMES { "MY TRACE", "MY DEBUG", "MY INFO", "MY WARNING", 131 | // "MY ERROR", "MY CRITICAL", "OFF" } 132 | /////////////////////////////////////////////////////////////////////////////// 133 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/include/spdlog/version.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_VER_MAJOR 1 9 | #define SPDLOG_VER_MINOR 1 10 | #define SPDLOG_VER_PATCH 0 11 | 12 | #define SPDLOG_VERSION (SPDLOG_VER_MAJOR * 10000 + SPDLOG_VER_MINOR * 100 + SPDLOG_VER_PATCH) 13 | -------------------------------------------------------------------------------- /real-app/deps/spdlog/spdlog-config.cmake: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | add_library(spdlog INTERFACE) 4 | target_include_directories(spdlog INTERFACE "${CMAKE_CURRENT_LIST_DIR}/include") 5 | -------------------------------------------------------------------------------- /real-app/res/app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/miniant-git/REAL/077e6fe2e6242f41bf401960562ccfecc6ca6a18/real-app/res/app.ico -------------------------------------------------------------------------------- /real-app/res/real-app.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/miniant-git/REAL/077e6fe2e6242f41bf401960562ccfecc6ca6a18/real-app/res/real-app.rc -------------------------------------------------------------------------------- /real-app/res/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by real-app.rc 4 | // 5 | #define IDI_ICON1 110 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 111 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /real-app/run-cmake.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | cmake -B build -G "Visual Studio 15 2017 Win64" . 4 | pause 5 | -------------------------------------------------------------------------------- /real-app/src/AutoUpdater.cpp: -------------------------------------------------------------------------------- 1 | #include "AutoUpdater.h" 2 | 3 | #include "Windows/Filesystem.h" 4 | 5 | #include "CurlWrapper/Writers/CurlFileWriter.h" 6 | #include "CurlWrapper/Writers/CurlMemoryWriter.h" 7 | 8 | #include 9 | 10 | using namespace miniant::AutoUpdater; 11 | using namespace miniant::CurlWrapper; 12 | using namespace miniant::Windows::Filesystem; 13 | 14 | using json = nlohmann::json; 15 | 16 | tl::expected, AutoUpdaterError> GetUpdaterRelease(CurlHandle& curl) { 17 | curl.Reset(); 18 | curl.SetUrl("https://api.github.com/repos/miniant-git/REAL/releases/tags/updater-v3"); 19 | curl.SetUserAgent("real_updater_v2"); 20 | 21 | CurlMemoryWriter memoryWriter; 22 | tl::expected responseCode = memoryWriter.InitiateRequest(curl); 23 | if (!responseCode) { 24 | return tl::make_unexpected(AutoUpdaterError(responseCode.error())); 25 | } 26 | 27 | if (*responseCode != 200) { 28 | return tl::make_unexpected(AutoUpdaterError("GET request failed.")); 29 | } 30 | 31 | json response = json::parse(memoryWriter.GetBuffer()); 32 | tl::expected version = Version::Find(response["name"]); 33 | if (!version) { 34 | return tl::make_unexpected(AutoUpdaterError(version.error())); 35 | } 36 | 37 | return { { std::move(*version), std::move(response) } }; 38 | } 39 | 40 | tl::expected, AutoUpdaterError> GetUpdateRelease(CurlHandle& curl) { 41 | tl::expected updaterRelease = GetUpdaterRelease(curl); 42 | if (updaterRelease) { 43 | return updaterRelease; 44 | } 45 | 46 | curl.Reset(); 47 | curl.SetUrl("https://api.github.com/repos/miniant-git/REAL/releases/latest"); 48 | curl.SetUserAgent("real_updater_v2"); 49 | 50 | CurlMemoryWriter memoryWriter; 51 | tl::expected responseCode = memoryWriter.InitiateRequest(curl); 52 | if (!responseCode) { 53 | return tl::make_unexpected(AutoUpdaterError(responseCode.error())); 54 | } 55 | 56 | if (*responseCode != 200) { 57 | return tl::make_unexpected(AutoUpdaterError("GET request failed.")); 58 | } 59 | 60 | json response = json::parse(memoryWriter.GetBuffer()); 61 | tl::expected latestVersion = Version::Find(response["name"]); 62 | if (!latestVersion) { 63 | return tl::make_unexpected(AutoUpdaterError(latestVersion.error())); 64 | } 65 | 66 | return { { std::move(*latestVersion), std::move(response) } }; 67 | } 68 | 69 | tl::expected FindUpdateAssetUrl(const json& response) { 70 | for (const auto& asset : response["assets"]) { 71 | if (asset["name"] == "update") { 72 | return { asset["browser_download_url"] }; 73 | } 74 | } 75 | 76 | return tl::make_unexpected(AutoUpdaterError("Could not find update asset URL.")); 77 | } 78 | 79 | WindowsString GetAppTempDirectory() { 80 | return GetTempDirectory() + L"miniant\\REAL\\"; 81 | } 82 | 83 | tl::expected GetReleaseNotes(const json& body) { 84 | static const std::string notesStartMarker("\r\n[//]: # (begin_release_notes)"); 85 | static const std::string notesEndMarker("\r\n[//]: # (end_release_notes)"); 86 | 87 | std::string bodyString(body); 88 | size_t notesStart = bodyString.find(notesStartMarker); 89 | size_t notesEnd = bodyString.rfind(notesEndMarker); 90 | 91 | if (notesStart == std::string::npos || notesEnd == std::string::npos) { 92 | return tl::make_unexpected(AutoUpdaterError("Could not find release notes.")); 93 | } 94 | 95 | notesStart += notesStartMarker.length(); 96 | return bodyString.substr(notesStart, notesEnd - notesStart); 97 | } 98 | 99 | AutoUpdater::AutoUpdater() { 100 | CurlHandle::InitialiseCurl(); 101 | } 102 | AutoUpdater::~AutoUpdater() { 103 | CurlHandle::CleanupCurl(); 104 | } 105 | 106 | std::optional AutoUpdater::IsAppSuperseded() { 107 | tl::expected curl = CurlHandle::Create(); 108 | if (!curl) { 109 | return {}; 110 | } 111 | 112 | curl->SetUrl("https://api.github.com/repos/miniant-git/REAL/releases/tags/superseded"); 113 | curl->SetUserAgent("real_updater_v2"); 114 | 115 | CurlMemoryWriter memoryWriter; 116 | tl::expected responseCode = memoryWriter.InitiateRequest(*curl); 117 | if (!responseCode) { 118 | return {}; 119 | } 120 | 121 | if (*responseCode != 200) { 122 | return {}; 123 | } 124 | 125 | json response = json::parse(memoryWriter.GetBuffer()); 126 | auto notes = GetReleaseNotes(response["body"]); 127 | if (!notes) { 128 | return {}; 129 | } 130 | 131 | return *notes; 132 | } 133 | 134 | tl::expected AutoUpdater::CleanupPreviousSetup() { 135 | const WindowsString executableToDelete = GetExecutablePath() + L"~DELETE"; 136 | if (IsFile(executableToDelete)) { 137 | if (!DeleteFile(executableToDelete)) { 138 | return tl::make_unexpected(AutoUpdaterError("Could not delete temporary file.")); 139 | } 140 | 141 | return true; 142 | } 143 | 144 | return false; 145 | } 146 | 147 | tl::expected AutoUpdater::GetUpdateInfo() const { 148 | tl::expected curl = CurlHandle::Create(); 149 | if (!curl) { 150 | return tl::make_unexpected(AutoUpdaterError(curl.error())); 151 | } 152 | 153 | tl::expected release = GetUpdateRelease(*curl); 154 | if (!release) { 155 | return tl::make_unexpected(AutoUpdaterError(release.error())); 156 | } 157 | 158 | auto[version, response] = std::move(*release); 159 | tl::expected downloadUrl = FindUpdateAssetUrl(response); 160 | if (!downloadUrl) { 161 | return tl::make_unexpected(AutoUpdaterError(downloadUrl.error())); 162 | } 163 | 164 | UpdateInfo info; 165 | info.version = std::move(version); 166 | info.downloadUrl = std::move(*downloadUrl); 167 | if (tl::expected releaseNotes = GetReleaseNotes(response["body"]); releaseNotes) { 168 | info.releaseNotes = *releaseNotes; 169 | } 170 | 171 | return info; 172 | } 173 | 174 | tl::expected AutoUpdater::ApplyUpdate(const UpdateInfo& info) const { 175 | tl::expected curl = CurlHandle::Create(); 176 | if (!curl) { 177 | return tl::make_unexpected(AutoUpdaterError(curl.error())); 178 | } 179 | 180 | curl->SetUrl(info.downloadUrl); 181 | curl->FollowRedirects(true); 182 | 183 | WindowsString tempDirectory = GetAppTempDirectory(); 184 | if (!CreateDirectory(tempDirectory)) { 185 | return tl::make_unexpected(AutoUpdaterError("Could not create temporary app directory.")); 186 | } 187 | 188 | std::filesystem::path updateFile(tempDirectory + L"update"); 189 | CurlFileWriter fileWriter(updateFile); 190 | fileWriter.InitiateRequest(*curl); 191 | fileWriter.Close(); 192 | 193 | WindowsString executable = GetExecutablePath(); 194 | std::optional executableDirectory = GetParentDirectory(executable); 195 | if (!executableDirectory) { 196 | return tl::make_unexpected(AutoUpdaterError("Could not get the application's executable file directory.")); 197 | } 198 | 199 | WindowsString renameExecutableCommand = GetRenameCommand(executable, L"REAL.exe~DELETE"); 200 | WindowsString renameZipCommand = GetRenameCommand(updateFile, L"update.zip"); 201 | updateFile.replace_extension(".zip"); 202 | WindowsString extractCommand = GetExtractZipCommand(updateFile, *executableDirectory); 203 | WindowsString deleteCommand = GetDeleteCommand(updateFile); 204 | if (!ExecuteCommand( 205 | renameExecutableCommand + L" && " + renameZipCommand + L" && " + extractCommand + L" && " + deleteCommand, 206 | !CanWriteTo(executable) || !CanWriteTo(*executableDirectory))) { 207 | return tl::make_unexpected(AutoUpdaterError("A filesystem error was encountered during the update procedure.")); 208 | } 209 | 210 | return {}; 211 | } 212 | -------------------------------------------------------------------------------- /real-app/src/AutoUpdater.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ExpectedError.h" 4 | #include "Version.h" 5 | 6 | #include 7 | 8 | namespace miniant::AutoUpdater { 9 | 10 | struct UpdateInfo { 11 | Version version; 12 | std::string downloadUrl; 13 | std::optional releaseNotes; 14 | }; 15 | 16 | class AutoUpdaterError : public ExpectedError { 17 | public: 18 | explicit AutoUpdaterError(std::string message) noexcept: 19 | ExpectedError(std::move(message)) {} 20 | 21 | explicit AutoUpdaterError(const char* message): 22 | ExpectedError(message) {} 23 | 24 | explicit AutoUpdaterError(const ExpectedError& error): 25 | ExpectedError(error.GetMessage()) {} 26 | }; 27 | 28 | class AutoUpdater { 29 | public: 30 | AutoUpdater(); 31 | ~AutoUpdater(); 32 | 33 | std::optional IsAppSuperseded(); 34 | 35 | tl::expected CleanupPreviousSetup(); 36 | tl::expected GetUpdateInfo() const; 37 | tl::expected ApplyUpdate(const UpdateInfo& info) const; 38 | }; 39 | 40 | } 41 | -------------------------------------------------------------------------------- /real-app/src/CurlWrapper/CurlError.cpp: -------------------------------------------------------------------------------- 1 | #include "CurlError.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | using namespace miniant::CurlWrapper; 8 | 9 | CurlError::CurlError(int curlCode): 10 | ExpectedError(curl_easy_strerror(static_cast(curlCode))) { 11 | assert(curlCode >= 0 && curlCode < CURL_LAST); 12 | } 13 | -------------------------------------------------------------------------------- /real-app/src/CurlWrapper/CurlError.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../ExpectedError.h" 4 | 5 | namespace miniant::CurlWrapper { 6 | 7 | class CurlError : public ExpectedError { 8 | public: 9 | explicit CurlError(std::string message) noexcept: 10 | ExpectedError(std::move(message)) {} 11 | 12 | explicit CurlError(const char* message): 13 | ExpectedError(message) {} 14 | 15 | explicit CurlError(int curlCode); 16 | }; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /real-app/src/CurlWrapper/CurlHandle.cpp: -------------------------------------------------------------------------------- 1 | #include "CurlHandle.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | using namespace miniant; 8 | using namespace miniant::CurlWrapper; 9 | 10 | CurlError CreateCurlError(const char* errorBuffer, CURLcode errorCode) { 11 | assert(errorBuffer != nullptr); 12 | 13 | if (strlen(errorBuffer) > 0) { 14 | return CurlError(errorBuffer); 15 | } 16 | 17 | return CurlError(errorCode); 18 | } 19 | 20 | CurlHandle::CurlHandle(CURL* curl, std::unique_ptr&& errorBuffer) noexcept: 21 | m_curl(curl), m_errorBuffer(std::move(errorBuffer)) {} 22 | 23 | CurlHandle::CurlHandle(CurlHandle&& curl) noexcept: 24 | m_curl(curl.m_curl), 25 | m_errorBuffer(std::move(curl.m_errorBuffer)) { 26 | assert(curl.m_curl != nullptr); 27 | curl.m_curl = nullptr; 28 | } 29 | 30 | CurlHandle::~CurlHandle() { 31 | if (m_curl != nullptr) { 32 | curl_easy_cleanup(m_curl); 33 | } 34 | } 35 | 36 | CurlHandle& CurlHandle::operator= (CurlHandle&& rhs) noexcept { 37 | assert(rhs.m_curl != nullptr); 38 | 39 | m_curl = rhs.m_curl; 40 | rhs.m_curl = nullptr; 41 | m_errorBuffer = std::move(rhs.m_errorBuffer); 42 | return *this; 43 | } 44 | 45 | tl::expected CurlHandle::SetUrl(const std::string& url) { 46 | assert(m_curl != nullptr); 47 | 48 | CURLcode errorCode = curl_easy_setopt(m_curl, CURLOPT_URL, url.c_str()); 49 | if (errorCode != CURLE_OK) { 50 | return tl::make_unexpected(CreateCurlError(m_errorBuffer.get(), errorCode)); 51 | } 52 | 53 | return {}; 54 | } 55 | 56 | tl::expected CurlHandle::SetUserAgent(const std::string& userAgent) { 57 | assert(m_curl != nullptr); 58 | 59 | CURLcode errorCode = curl_easy_setopt(m_curl, CURLOPT_USERAGENT, userAgent.c_str()); 60 | if (errorCode != CURLE_OK) { 61 | return tl::make_unexpected(CreateCurlError(m_errorBuffer.get(), errorCode)); 62 | } 63 | 64 | return {}; 65 | } 66 | 67 | tl::expected CurlHandle::Perform(void* userData, write_callback callback) { 68 | assert(m_curl != nullptr); 69 | 70 | if (CURLcode errorCode = curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, userData); errorCode != CURLE_OK) { 71 | return tl::make_unexpected(CreateCurlError(m_errorBuffer.get(), errorCode)); 72 | } 73 | 74 | if (CURLcode errorCode = curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, callback); errorCode != CURLE_OK) { 75 | return tl::make_unexpected(CreateCurlError(m_errorBuffer.get(), errorCode)); 76 | } 77 | 78 | if (CURLcode errorCode = curl_easy_perform(m_curl); errorCode != CURLE_OK) { 79 | return tl::make_unexpected(CreateCurlError(m_errorBuffer.get(), errorCode)); 80 | } 81 | 82 | long responseCode; 83 | if (CURLcode errorCode = curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &responseCode); errorCode != CURLE_OK) { 84 | return tl::make_unexpected(CreateCurlError(m_errorBuffer.get(), errorCode)); 85 | } 86 | 87 | return responseCode; 88 | } 89 | 90 | tl::expected CurlHandle::FollowRedirects(bool enabled) { 91 | assert(m_curl != nullptr); 92 | 93 | CURLcode errorCode = curl_easy_setopt(m_curl, CURLOPT_FOLLOWLOCATION, static_cast(enabled)); 94 | if (errorCode != CURLE_OK) { 95 | return tl::make_unexpected(CreateCurlError(m_errorBuffer.get(), errorCode)); 96 | } 97 | 98 | return {}; 99 | } 100 | 101 | void CurlHandle::Reset() { 102 | assert(m_curl != nullptr); 103 | curl_easy_reset(m_curl); 104 | } 105 | 106 | tl::expected CurlHandle::Create() { 107 | CURL* curl = curl_easy_init(); 108 | if (curl == NULL) { 109 | return tl::make_unexpected(CurlError("Failed to create curl easy handle.")); 110 | } 111 | 112 | auto errorBuffer = std::make_unique(CURL_ERROR_SIZE); 113 | if (curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorBuffer.get()) != CURLE_OK) { 114 | return tl::make_unexpected(CurlError("Failed to assign error buffer to curl handle.")); 115 | } 116 | 117 | return CurlHandle(curl, std::move(errorBuffer)); 118 | } 119 | 120 | void CurlHandle::InitialiseCurl() { 121 | curl_global_init(CURL_GLOBAL_ALL); 122 | } 123 | 124 | void CurlHandle::CleanupCurl() { 125 | curl_global_cleanup(); 126 | } 127 | -------------------------------------------------------------------------------- /real-app/src/CurlWrapper/CurlHandle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CurlError.h" 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace miniant::CurlWrapper { 10 | 11 | class CurlHandle { 12 | public: 13 | using write_callback = size_t (*)(char* ptr, size_t size, size_t nmemb, void* userdata); 14 | 15 | CurlHandle(CurlHandle&& curl) noexcept; 16 | ~CurlHandle(); 17 | 18 | CurlHandle& operator= (CurlHandle&& rhs) noexcept; 19 | 20 | tl::expected SetUrl(const std::string& url); 21 | tl::expected SetUserAgent(const std::string& userAgent); 22 | 23 | tl::expected Perform(void* userData, write_callback callback); 24 | 25 | tl::expected FollowRedirects(bool enabled); 26 | void Reset(); 27 | 28 | static tl::expected Create(); 29 | 30 | static void InitialiseCurl(); 31 | static void CleanupCurl(); 32 | 33 | private: 34 | explicit CurlHandle(void* curl, std::unique_ptr&& errorBuffer) noexcept; 35 | 36 | void* m_curl; 37 | std::unique_ptr m_errorBuffer; 38 | }; 39 | 40 | } 41 | -------------------------------------------------------------------------------- /real-app/src/CurlWrapper/Writers/CurlFileWriter.cpp: -------------------------------------------------------------------------------- 1 | #include "CurlFileWriter.h" 2 | 3 | using namespace miniant::CurlWrapper; 4 | 5 | size_t WriteToFile(char* ptr, size_t size, size_t nmemb, void* userdata) { 6 | auto file = static_cast(userdata); 7 | if (file->is_open()) { 8 | file->write(ptr, nmemb); 9 | return nmemb; 10 | } 11 | 12 | return 0; 13 | } 14 | 15 | CurlFileWriter::CurlFileWriter(std::filesystem::path filepath): 16 | m_file(filepath, std::ofstream::binary) {} 17 | 18 | void CurlFileWriter::Close() { 19 | m_file.close(); 20 | } 21 | 22 | CurlHandle::write_callback CurlFileWriter::GetWriteCallback() { 23 | return &WriteToFile; 24 | } 25 | 26 | void* CurlFileWriter::GetWriteData() { 27 | return &m_file; 28 | } 29 | -------------------------------------------------------------------------------- /real-app/src/CurlWrapper/Writers/CurlFileWriter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CurlWriter.h" 4 | 5 | #include 6 | #include 7 | 8 | namespace miniant::CurlWrapper { 9 | 10 | class CurlFileWriter : public CurlWriter { 11 | public: 12 | CurlFileWriter(std::filesystem::path filepath); 13 | 14 | void Close(); 15 | 16 | protected: 17 | virtual CurlHandle::write_callback GetWriteCallback() override; 18 | virtual void* GetWriteData() override; 19 | 20 | private: 21 | std::ofstream m_file; 22 | }; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /real-app/src/CurlWrapper/Writers/CurlMemoryWriter.cpp: -------------------------------------------------------------------------------- 1 | #include "CurlMemoryWriter.h" 2 | 3 | using namespace miniant::CurlWrapper; 4 | 5 | size_t WriteToBuffer(char* ptr, size_t size, size_t nmemb, void* userdata) { 6 | auto buffer = reinterpret_cast*>(userdata); 7 | buffer->insert(buffer->end(), ptr, ptr+nmemb); 8 | return nmemb; 9 | } 10 | 11 | std::vector& CurlMemoryWriter::GetBuffer() { 12 | return m_buffer; 13 | } 14 | 15 | CurlHandle::write_callback CurlMemoryWriter::GetWriteCallback() { 16 | return &WriteToBuffer; 17 | } 18 | 19 | void* CurlMemoryWriter::GetWriteData() { 20 | return &m_buffer; 21 | } 22 | -------------------------------------------------------------------------------- /real-app/src/CurlWrapper/Writers/CurlMemoryWriter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CurlWriter.h" 4 | 5 | #include 6 | 7 | namespace miniant::CurlWrapper { 8 | 9 | class CurlMemoryWriter : public CurlWriter { 10 | public: 11 | std::vector& GetBuffer(); 12 | 13 | protected: 14 | virtual CurlHandle::write_callback GetWriteCallback() override; 15 | virtual void* GetWriteData() override; 16 | 17 | private: 18 | std::vector m_buffer; 19 | }; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /real-app/src/CurlWrapper/Writers/CurlWriter.cpp: -------------------------------------------------------------------------------- 1 | #include "CurlWriter.h" 2 | 3 | using namespace miniant::CurlWrapper; 4 | 5 | tl::expected CurlWriter::InitiateRequest(CurlHandle& handle) { 6 | return handle.Perform(GetWriteData(), GetWriteCallback()); 7 | } 8 | -------------------------------------------------------------------------------- /real-app/src/CurlWrapper/Writers/CurlWriter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../CurlHandle.h" 4 | 5 | namespace miniant::CurlWrapper { 6 | 7 | class CurlWriter { 8 | public: 9 | virtual ~CurlWriter() = default; 10 | 11 | tl::expected InitiateRequest(CurlHandle& handle); 12 | 13 | protected: 14 | virtual CurlHandle::write_callback GetWriteCallback() = 0; 15 | virtual void* GetWriteData() = 0; 16 | }; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /real-app/src/ExpectedError.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace miniant { 6 | 7 | class ExpectedError { 8 | public: 9 | explicit ExpectedError(std::string message) noexcept: 10 | m_message(std::move(message)) {} 11 | 12 | explicit ExpectedError(const char* message): 13 | m_message(message) {} 14 | 15 | ExpectedError(const ExpectedError&) = default; 16 | ExpectedError(ExpectedError&&) noexcept = default; 17 | 18 | ExpectedError& operator= (const ExpectedError&) = default; 19 | ExpectedError& operator= (ExpectedError&&) noexcept = default; 20 | 21 | virtual ~ExpectedError() = default; 22 | 23 | virtual std::string GetMessage() const { 24 | return m_message; 25 | } 26 | 27 | private: 28 | std::string m_message; 29 | }; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /real-app/src/OStreamSink.cpp: -------------------------------------------------------------------------------- 1 | #include "OStreamSink.h" 2 | 3 | using namespace miniant::Spdlog; 4 | 5 | OStreamSink::OStreamSink(std::shared_ptr outputStream, bool forceFlush): 6 | m_outputStream(std::move(outputStream)), 7 | m_forceFlush(forceFlush) {} 8 | 9 | std::mutex& OStreamSink::GetMutex() { 10 | return mutex_; 11 | } 12 | 13 | void OStreamSink::sink_it_(const spdlog::details::log_msg& message) { 14 | fmt::memory_buffer formattedMessage; 15 | sink::formatter_->format(message, formattedMessage); 16 | m_outputStream->write(formattedMessage.data(), static_cast(formattedMessage.size())); 17 | if (m_forceFlush) 18 | m_outputStream->flush(); 19 | } 20 | 21 | void OStreamSink::flush_() { 22 | m_outputStream->flush(); 23 | } 24 | -------------------------------------------------------------------------------- /real-app/src/OStreamSink.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace miniant::Spdlog { 8 | 9 | class OStreamSink : public spdlog::sinks::base_sink { 10 | public: 11 | explicit OStreamSink(std::shared_ptr outputStream, bool forceFlush=false); 12 | 13 | std::mutex& GetMutex(); 14 | 15 | protected: 16 | void sink_it_(const spdlog::details::log_msg& message) override; 17 | void flush_() override; 18 | 19 | private: 20 | std::shared_ptr m_outputStream; 21 | bool m_forceFlush; 22 | }; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /real-app/src/Version.cpp: -------------------------------------------------------------------------------- 1 | #include "Version.h" 2 | 3 | #include 4 | #include 5 | 6 | using namespace miniant::AutoUpdater; 7 | 8 | const std::regex VERSION_PATTERN( 9 | R"(v(\d+)\.(\d+)\.(\d+))", 10 | std::regex_constants::ECMAScript | std::regex_constants::optimize); 11 | 12 | std::string Version::ToString() const { 13 | std::stringstream ss; 14 | ss << 'v' << m_major << '.' << m_minor << '.' << m_patch; 15 | return ss.str(); 16 | } 17 | 18 | bool Version::operator< (const Version& rhs) const noexcept { 19 | if (m_major != rhs.m_major) { 20 | return m_major < rhs.m_major; 21 | } 22 | 23 | if (m_minor != rhs.m_minor) { 24 | return m_minor < rhs.m_minor; 25 | } 26 | 27 | return m_patch < rhs.m_patch; 28 | } 29 | 30 | bool Version::operator> (const Version& rhs) const noexcept { 31 | return rhs < *this; 32 | } 33 | 34 | bool Version::operator==(const Version& rhs) const noexcept { 35 | return m_major == rhs.m_major && m_minor == rhs.m_minor && m_patch == rhs.m_patch; 36 | } 37 | 38 | bool Version::operator!=(const Version& rhs) const noexcept { 39 | return !(*this == rhs); 40 | } 41 | 42 | bool Version::operator<=(const Version& rhs) const noexcept { 43 | return *this == rhs || *this < rhs; 44 | } 45 | 46 | bool Version::operator>=(const Version& rhs) const noexcept { 47 | return *this == rhs || *this > rhs; 48 | } 49 | 50 | tl::expected Version::Parse(const std::string& versionString) { 51 | std::smatch matches; 52 | if (!std::regex_match(versionString, matches, VERSION_PATTERN)) { 53 | return tl::make_unexpected(VersionError("Failed to parse version string.")); 54 | } 55 | 56 | int major = std::stoi(matches[1].str()); 57 | int minor = std::stoi(matches[2].str()); 58 | int patch = std::stoi(matches[3].str()); 59 | if (major > UINT16_MAX || minor > UINT16_MAX || patch > UINT16_MAX) { 60 | return tl::make_unexpected(VersionError("Failed to parse version string.")); 61 | } 62 | 63 | return Version( 64 | static_cast(major), 65 | static_cast(minor), 66 | static_cast(patch)); 67 | } 68 | 69 | tl::expected Version::Find(const std::string& string) { 70 | std::smatch matches; 71 | if (!std::regex_search(string, matches, VERSION_PATTERN)) 72 | return tl::make_unexpected(VersionError("String does not contain properly formatted version.")); 73 | 74 | return Version::Parse(matches[0].str()); 75 | } 76 | -------------------------------------------------------------------------------- /real-app/src/Version.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ExpectedError.h" 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace miniant::AutoUpdater { 10 | 11 | class VersionError : public ExpectedError { 12 | public: 13 | explicit VersionError(const char* message): 14 | ExpectedError(message) {} 15 | }; 16 | 17 | class Version { 18 | public: 19 | constexpr Version() noexcept: 20 | m_major(0), m_minor(0), m_patch(0) {}; 21 | 22 | constexpr Version(uint16_t major, uint16_t minor, uint16_t patch) noexcept: 23 | m_major(major), m_minor(minor), m_patch(patch) {} 24 | 25 | std::string ToString() const; 26 | 27 | bool operator==(const Version& rhs) const noexcept; 28 | bool operator!=(const Version& rhs) const noexcept; 29 | bool operator< (const Version& rhs) const noexcept; 30 | bool operator<=(const Version& rhs) const noexcept; 31 | bool operator> (const Version& rhs) const noexcept; 32 | bool operator>=(const Version& rhs) const noexcept; 33 | 34 | static tl::expected Parse(const std::string& versionString); 35 | static tl::expected Find(const std::string& string); 36 | 37 | private: 38 | uint16_t m_major; 39 | uint16_t m_minor; 40 | uint16_t m_patch; 41 | }; 42 | 43 | } 44 | -------------------------------------------------------------------------------- /real-app/src/Windows/Console.cpp: -------------------------------------------------------------------------------- 1 | #include "Console.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | using namespace miniant::Windows; 8 | 9 | Console::Console(std::function onShow): 10 | m_onShow(std::move(onShow)) {} 11 | 12 | void Console::Open() { 13 | if (m_opened) { 14 | return; 15 | } 16 | 17 | ::AllocConsole(); 18 | 19 | FILE* dummy; 20 | freopen_s(&dummy, "conout$", "w", stdout); 21 | freopen_s(&dummy, "conin$", "r", stdin); 22 | 23 | std::cout.clear(); 24 | std::cin.clear(); 25 | 26 | if (m_onShow) { 27 | m_onShow(); 28 | } 29 | 30 | m_opened = true; 31 | } 32 | 33 | void Console::Close() { 34 | if (!m_opened) { 35 | return; 36 | } 37 | 38 | ::SendMessage(::GetConsoleWindow(), WM_CLOSE, 0, 0); 39 | ::FreeConsole(); 40 | 41 | m_opened = false; 42 | } 43 | -------------------------------------------------------------------------------- /real-app/src/Windows/Console.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace miniant::Windows { 6 | 7 | class Console { 8 | public: 9 | Console(std::function onShow); 10 | 11 | void Open(); 12 | void Close(); 13 | 14 | private: 15 | std::function m_onShow; 16 | bool m_opened = false; 17 | }; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /real-app/src/Windows/Filesystem.cpp: -------------------------------------------------------------------------------- 1 | #include "Filesystem.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | using namespace miniant::Windows::Filesystem; 9 | 10 | const WindowsString PATH_SEPARATORS(TEXT("\\/")); 11 | 12 | bool CanAccess(const WindowsString& path, DWORD accessRights) { 13 | SECURITY_INFORMATION RequestedInformation = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION; 14 | DWORD nLengthNeeded; 15 | ::GetFileSecurity(path.c_str(), RequestedInformation, NULL, 0, &nLengthNeeded); 16 | 17 | auto securityDescriptorMemory = std::make_unique(nLengthNeeded); 18 | PSECURITY_DESCRIPTOR pSecurityDescriptor = securityDescriptorMemory.get(); 19 | if (pSecurityDescriptor == nullptr) 20 | return false; 21 | 22 | if (!::GetFileSecurity(path.c_str(), RequestedInformation, pSecurityDescriptor, nLengthNeeded, &nLengthNeeded)) 23 | return false; 24 | 25 | HANDLE hToken; 26 | if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_DUPLICATE | STANDARD_RIGHTS_READ, &hToken)) 27 | return false; 28 | 29 | HANDLE hImpersonationToken; 30 | if (!::DuplicateToken(hToken, SecurityImpersonation, &hImpersonationToken)) { 31 | ::CloseHandle(hToken); 32 | return false; 33 | } 34 | 35 | GENERIC_MAPPING GenericMapping = {}; 36 | GenericMapping.GenericAll = FILE_ALL_ACCESS; 37 | GenericMapping.GenericExecute = FILE_GENERIC_EXECUTE; 38 | GenericMapping.GenericRead = FILE_GENERIC_READ; 39 | GenericMapping.GenericWrite = FILE_GENERIC_WRITE; 40 | 41 | ::MapGenericMask(&accessRights, &GenericMapping); 42 | 43 | PRIVILEGE_SET PrivilegeSet; 44 | DWORD PrivelegeSetLength = sizeof(PrivilegeSet); 45 | DWORD GrantedAccess; 46 | BOOL AccessStatus; 47 | bool result = ::AccessCheck( 48 | pSecurityDescriptor, 49 | hImpersonationToken, 50 | accessRights, 51 | &GenericMapping, 52 | &PrivilegeSet, 53 | &PrivelegeSetLength, 54 | &GrantedAccess, 55 | &AccessStatus); 56 | if (!result) { 57 | ::CloseHandle(hToken); 58 | ::CloseHandle(hImpersonationToken); 59 | return false; 60 | } 61 | 62 | ::CloseHandle(hToken); 63 | ::CloseHandle(hImpersonationToken); 64 | 65 | return AccessStatus == TRUE; 66 | } 67 | 68 | WindowsString MakePowerShellCommand(const WindowsString& command) { 69 | static const std::basic_regex backslash(TEXT(R"(\\)"), std::regex_constants::optimize); 70 | static const std::basic_regex doubleQuotes(TEXT(R"(")"), std::regex_constants::optimize); 71 | 72 | WindowsString escapedCommand = std::regex_replace(command, backslash, TEXT(R"(\\)")); 73 | escapedCommand = std::regex_replace(escapedCommand, doubleQuotes, TEXT(R"(\")")); 74 | return TEXT("powershell -Command \"&{ $ErrorActionPreference = 'Stop'; ") + escapedCommand + TEXT("; trap { exit 1 }}\""); 75 | } 76 | 77 | namespace miniant::Windows::Filesystem { 78 | 79 | WindowsString WrapInDoubleQuotes(const WindowsString& string) { 80 | return TEXT("\"") + string + TEXT("\""); 81 | } 82 | 83 | bool ExecuteCommand(const WindowsString& command, bool asAdministrator) { 84 | WindowsString parameters = TEXT("/C ") + WrapInDoubleQuotes(command); 85 | 86 | SHELLEXECUTEINFO info = {}; 87 | info.cbSize = sizeof(SHELLEXECUTEINFO); 88 | info.fMask = SEE_MASK_NOCLOSEPROCESS; 89 | info.lpVerb = asAdministrator ? TEXT("runas") : TEXT(""); 90 | info.lpFile = TEXT("cmd"); 91 | info.lpParameters = parameters.c_str(); 92 | info.nShow = SW_HIDE; 93 | if (::ShellExecuteEx(&info) == TRUE) { 94 | ::WaitForSingleObject(info.hProcess, INFINITE); 95 | DWORD exitCode; 96 | bool success = ::GetExitCodeProcess(info.hProcess, &exitCode); 97 | ::CloseHandle(info.hProcess); 98 | return success && exitCode == 0; 99 | } 100 | 101 | return false; 102 | } 103 | 104 | WindowsString GetExecutablePath() { 105 | std::vector path(128); 106 | do { 107 | path.resize(path.size() * 2); 108 | if (path.size() > DWORD(-1)) 109 | path.resize(DWORD(-1)); 110 | 111 | DWORD nPathLength = ::GetModuleFileName(NULL, path.data(), static_cast(path.size())); 112 | if (nPathLength < path.size()) 113 | break; 114 | 115 | } while (::GetLastError() == ERROR_INSUFFICIENT_BUFFER && path.size() < DWORD(-1)); 116 | 117 | return path.data(); 118 | } 119 | 120 | WindowsString GetTempDirectory() { 121 | DWORD pathLength = ::GetTempPath(0, NULL); 122 | std::vector path(pathLength); 123 | ::GetTempPath(pathLength, path.data()); 124 | return path.data(); 125 | } 126 | 127 | std::optional> GetPathComponents(const WindowsString& path) { 128 | if (path.empty()) 129 | return {}; 130 | 131 | std::vector components; 132 | size_t pathLength = path.length(); 133 | size_t lastPosition = 0; 134 | while (lastPosition < pathLength) { 135 | size_t position = path.find_first_of(PATH_SEPARATORS, lastPosition); 136 | if (position - lastPosition > 0) 137 | components.push_back(path.substr(lastPosition, position - lastPosition)); 138 | 139 | if (position == WindowsString::npos) 140 | break; 141 | 142 | lastPosition = position + 1; 143 | } 144 | 145 | return { std::move(components) }; 146 | } 147 | 148 | std::optional GetName(const WindowsString& path) { 149 | size_t lastSeparator = path.find_last_of(PATH_SEPARATORS); 150 | if (lastSeparator == WindowsString::npos) 151 | return path; 152 | 153 | if (lastSeparator == path.length() - 1) { 154 | size_t secondLastSeparator = path.find_last_of(PATH_SEPARATORS, 1); 155 | if (secondLastSeparator == WindowsString::npos) 156 | return path.substr(0, lastSeparator); 157 | 158 | return path.substr(secondLastSeparator + 1, lastSeparator); 159 | } 160 | 161 | return path.substr(lastSeparator + 1); 162 | } 163 | 164 | std::optional GetParentDirectory(const WindowsString& path) { 165 | size_t lastSeparator = path.find_last_of(PATH_SEPARATORS); 166 | if (lastSeparator == WindowsString::npos) 167 | return {}; 168 | 169 | return { path.substr(0, lastSeparator + 1) }; 170 | } 171 | 172 | bool IsDirectory(const WindowsString& path) { 173 | DWORD attributes = ::GetFileAttributes(path.c_str()); 174 | if (attributes == INVALID_FILE_ATTRIBUTES) 175 | return false; 176 | 177 | return attributes & FILE_ATTRIBUTE_DIRECTORY; 178 | } 179 | 180 | bool IsFile(const WindowsString& path) { 181 | DWORD attributes = ::GetFileAttributes(path.c_str()); 182 | if (attributes == INVALID_FILE_ATTRIBUTES) 183 | return false; 184 | 185 | return !(attributes & FILE_ATTRIBUTE_DIRECTORY); 186 | } 187 | 188 | WindowsString GetDeleteCommand(const WindowsString& filepath) { 189 | return TEXT("del /F /Q ") + WrapInDoubleQuotes(filepath); 190 | } 191 | 192 | WindowsString GetMoveCommand(const WindowsString& source, const WindowsString& destination) { 193 | return TEXT("move /Y ") + WrapInDoubleQuotes(source) + TEXT(" ") + WrapInDoubleQuotes(destination); 194 | } 195 | 196 | WindowsString GetRenameCommand(const WindowsString& source, const WindowsString& newName) { 197 | return TEXT("rename ") + WrapInDoubleQuotes(source) + TEXT(" ") + WrapInDoubleQuotes(newName); 198 | } 199 | 200 | WindowsString GetExtractZipCommand(const WindowsString& zipfile, const WindowsString& destination) { 201 | return MakePowerShellCommand(TEXT("Expand-Archive -Path ") + WrapInDoubleQuotes(zipfile) 202 | + TEXT(" -DestinationPath ") + WrapInDoubleQuotes(destination)); 203 | } 204 | 205 | bool CanWriteTo(const WindowsString& path) { 206 | return CanAccess(path, GENERIC_WRITE); 207 | } 208 | 209 | #undef DeleteFile 210 | bool DeleteFile(const WindowsString& filepath) { 211 | return ExecuteCommand(GetDeleteCommand(filepath), !CanWriteTo(filepath)); 212 | } 213 | 214 | #undef MoveFile 215 | bool MoveFile(const WindowsString& source, const WindowsString& destination) { 216 | std::optional parentDirectory = GetParentDirectory(destination); 217 | return ExecuteCommand( 218 | GetMoveCommand(source, destination), 219 | !CanWriteTo(source) || !parentDirectory || !CanWriteTo(parentDirectory.value())); 220 | } 221 | 222 | bool RenameFile(const WindowsString& source, const WindowsString& newName) { 223 | return ExecuteCommand(GetRenameCommand(source, newName), !CanWriteTo(source)); 224 | } 225 | 226 | #pragma push_macro("CreateDirectory") 227 | #undef CreateDirectory 228 | bool CreateDirectory(const WindowsString& path) { 229 | #pragma pop_macro("CreateDirectory") 230 | 231 | if (IsDirectory(path)) 232 | return true; 233 | 234 | std::optional> components = GetPathComponents(path); 235 | if (!components) 236 | return false; 237 | 238 | WindowsString currentPath = TEXT(""); 239 | for (const auto& component : components.value()) { 240 | currentPath += component + TEXT('\\'); 241 | ::CreateDirectory(currentPath.c_str(), NULL); 242 | } 243 | 244 | return true; 245 | } 246 | 247 | bool ExtractZip(const WindowsString& zipfile, const WindowsString& destination) { 248 | return ExecuteCommand(GetExtractZipCommand(zipfile, destination), !CanWriteTo(destination)); 249 | } 250 | 251 | } 252 | -------------------------------------------------------------------------------- /real-app/src/Windows/Filesystem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace miniant::Windows::Filesystem { 8 | 9 | #ifdef _UNICODE 10 | using WindowsString = std::wstring; 11 | #else 12 | using WindowsString = std::string; 13 | #endif 14 | 15 | WindowsString WrapInDoubleQuotes(const WindowsString& string); 16 | bool ExecuteCommand(const WindowsString& command, bool asAdministrator); 17 | 18 | WindowsString GetExecutablePath(); 19 | WindowsString GetTempDirectory(); 20 | 21 | std::optional> GetPathComponents(const WindowsString& path); 22 | std::optional GetName(const WindowsString& path); 23 | std::optional GetParentDirectory(const WindowsString& path); 24 | 25 | bool IsDirectory(const WindowsString& path); 26 | bool IsFile(const WindowsString& path); 27 | 28 | WindowsString GetDeleteCommand(const WindowsString& filepath); 29 | WindowsString GetMoveCommand(const WindowsString& source, const WindowsString& destination); 30 | WindowsString GetRenameCommand(const WindowsString& source, const WindowsString& newName); 31 | WindowsString GetExtractZipCommand(const WindowsString& zipfile, const WindowsString& destination); 32 | 33 | bool CanWriteTo(const WindowsString& path); 34 | 35 | bool MoveFile(const WindowsString& source, const WindowsString& destination); 36 | bool RenameFile(const WindowsString& source, const WindowsString& newName); 37 | bool DeleteFile(const WindowsString& filepath); 38 | 39 | bool CreateDirectory(const WindowsString& path); 40 | bool ExtractZip(const WindowsString& zipfile, const WindowsString& destination); 41 | } 42 | -------------------------------------------------------------------------------- /real-app/src/Windows/GlobalWindowProcedure.cpp: -------------------------------------------------------------------------------- 1 | #include "GlobalWindowProcedure.h" 2 | 3 | #include 4 | 5 | using namespace miniant::Windows; 6 | 7 | std::unordered_map GlobalWindowProcedure::s_windowProcedureMap; 8 | 9 | UINT GlobalWindowProcedure::GetFreeEventId() noexcept { 10 | static UINT nextFreeEventId = WM_USER; 11 | return nextFreeEventId++; 12 | } 13 | 14 | tl::expected GlobalWindowProcedure::RegisterWindowClass(LPCTSTR lpszClassName) { 15 | assert(lpszClassName != nullptr); 16 | 17 | WNDCLASS wc = {}; 18 | wc.lpszClassName = lpszClassName; 19 | wc.lpfnWndProc = &WndProc; 20 | if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast(&WndProc), &wc.hInstance)) { 21 | return tl::make_unexpected(WindowsError()); 22 | } 23 | 24 | if (::RegisterClass(&wc) == 0) { 25 | return tl::make_unexpected(WindowsError()); 26 | } 27 | 28 | return wc; 29 | } 30 | 31 | void GlobalWindowProcedure::SetWindowProcedure(HWND hWnd, WindowProcedure procedure) { 32 | s_windowProcedureMap[hWnd] = procedure; 33 | } 34 | 35 | LRESULT CALLBACK GlobalWindowProcedure::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { 36 | WindowProcedure* windowProcedure; 37 | try { 38 | windowProcedure = &s_windowProcedureMap.at(hWnd); 39 | } catch (std::out_of_range&) { 40 | return ::DefWindowProc(hWnd, uMsg, wParam, lParam); 41 | } 42 | 43 | if (*windowProcedure) { 44 | std::optional lResult = (*windowProcedure)(hWnd, uMsg, wParam, lParam); 45 | if (lResult) { 46 | return lResult.value(); 47 | } 48 | } 49 | 50 | return ::DefWindowProc(hWnd, uMsg, wParam, lParam); 51 | } 52 | -------------------------------------------------------------------------------- /real-app/src/Windows/GlobalWindowProcedure.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "WindowsError.h" 4 | 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | namespace miniant::Windows { 14 | 15 | class GlobalWindowProcedure { 16 | public: 17 | using WindowProcedure = std::function(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lparam)>; 18 | 19 | static UINT GetFreeEventId() noexcept; 20 | 21 | static tl::expected RegisterWindowClass(LPCTSTR lpszClassName); 22 | static void SetWindowProcedure(HWND hWnd, WindowProcedure procedure); 23 | 24 | private: 25 | static std::unordered_map s_windowProcedureMap; 26 | 27 | static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 28 | }; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /real-app/src/Windows/MessagingWindow.cpp: -------------------------------------------------------------------------------- 1 | #include "MessagingWindow.h" 2 | 3 | #include "GlobalWindowProcedure.h" 4 | 5 | #include 6 | 7 | using namespace miniant::Windows; 8 | 9 | constexpr TCHAR CLASS_NAME[] = TEXT("MessagingWindow"); 10 | 11 | tl::expected CreateNewWindow() { 12 | tl::expected wc = GlobalWindowProcedure::RegisterWindowClass(CLASS_NAME); 13 | if (!wc) { 14 | return tl::make_unexpected(std::move(wc.error())); 15 | } 16 | 17 | HWND hWnd = ::CreateWindowEx( 18 | 0, 19 | CLASS_NAME, 20 | NULL, 21 | 0, 22 | 0, 0, 0, 0, 23 | HWND_MESSAGE, 24 | NULL, 25 | wc->hInstance, 26 | NULL); 27 | if (hWnd == NULL) { 28 | return tl::make_unexpected(WindowsError()); 29 | } 30 | 31 | return hWnd; 32 | } 33 | 34 | tl::expected MessagingWindow::Create() { 35 | tl::expected hWnd = CreateNewWindow(); 36 | if (!hWnd) { 37 | return tl::make_unexpected(hWnd.error()); 38 | } 39 | 40 | return MessagingWindow(*hWnd); 41 | } 42 | 43 | tl::expected, WindowsError> MessagingWindow::CreatePtr() { 44 | tl::expected hWnd = CreateNewWindow(); 45 | if (!hWnd) { 46 | return tl::make_unexpected(hWnd.error()); 47 | } 48 | 49 | return std::unique_ptr(new MessagingWindow(*hWnd)); 50 | } 51 | 52 | MessagingWindow::MessagingWindow(HWND hWnd): 53 | m_hWnd(hWnd) { 54 | assert(m_hWnd != NULL); 55 | 56 | GlobalWindowProcedure::SetWindowProcedure(m_hWnd, [this](HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { 57 | auto eventHandler = m_eventHandlerMap.find(uMsg); 58 | if (eventHandler != m_eventHandlerMap.end()) { 59 | if (eventHandler->second) { 60 | return eventHandler->second(*this, wParam, lParam); 61 | } 62 | } 63 | 64 | return std::optional(); 65 | }); 66 | } 67 | 68 | MessagingWindow::MessagingWindow(MessagingWindow&& other): 69 | m_hWnd(other.m_hWnd), 70 | m_eventHandlerMap(std::move(other.m_eventHandlerMap)) { 71 | assert(other.m_hWnd != NULL); 72 | 73 | other.m_hWnd = NULL; 74 | 75 | GlobalWindowProcedure::SetWindowProcedure(m_hWnd, [this](HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { 76 | auto eventHandler = m_eventHandlerMap.find(uMsg); 77 | if (eventHandler != m_eventHandlerMap.end()) { 78 | if (eventHandler->second) { 79 | return eventHandler->second(*this, wParam, lParam); 80 | } 81 | } 82 | 83 | return std::optional(); 84 | }); 85 | } 86 | 87 | MessagingWindow::~MessagingWindow() { 88 | if (m_hWnd != NULL) { 89 | GlobalWindowProcedure::SetWindowProcedure(m_hWnd, nullptr); 90 | } 91 | } 92 | 93 | MessagingWindow& MessagingWindow::operator= (MessagingWindow&& rhs) { 94 | assert(rhs.m_hWnd != NULL); 95 | 96 | GlobalWindowProcedure::SetWindowProcedure(m_hWnd, nullptr); 97 | 98 | m_hWnd = rhs.m_hWnd; 99 | m_eventHandlerMap = std::move(rhs.m_eventHandlerMap); 100 | rhs.m_hWnd = NULL; 101 | 102 | GlobalWindowProcedure::SetWindowProcedure(m_hWnd, [this](HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { 103 | auto eventHandler = m_eventHandlerMap.find(uMsg); 104 | if (eventHandler != m_eventHandlerMap.end()) { 105 | if (eventHandler->second) { 106 | return eventHandler->second(*this, wParam, lParam); 107 | } 108 | } 109 | 110 | return std::optional(); 111 | }); 112 | 113 | return *this; 114 | } 115 | 116 | HWND MessagingWindow::GetHWindow() noexcept { 117 | assert(m_hWnd != NULL); 118 | return m_hWnd; 119 | } 120 | 121 | void MessagingWindow::SetEventHandler(UINT event, EventHandler handler) { 122 | assert(m_hWnd != NULL); 123 | assert(handler); 124 | m_eventHandlerMap[event] = std::move(handler); 125 | } 126 | 127 | void MessagingWindow::RemoveEventHandler(UINT event) { 128 | size_t numElementsRemoved = m_eventHandlerMap.erase(event); 129 | assert(numElementsRemoved > 0); 130 | } 131 | -------------------------------------------------------------------------------- /real-app/src/Windows/MessagingWindow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "WindowsError.h" 4 | 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | namespace miniant::Windows { 14 | 15 | class MessagingWindow { 16 | public: 17 | using EventHandler = std::function(MessagingWindow&, WPARAM, LPARAM)>; 18 | 19 | MessagingWindow(MessagingWindow&& other); 20 | ~MessagingWindow(); 21 | 22 | MessagingWindow& operator= (MessagingWindow&& rhs); 23 | 24 | HWND GetHWindow() noexcept; 25 | 26 | void SetEventHandler(UINT event, EventHandler handler); 27 | void RemoveEventHandler(UINT event); 28 | 29 | static tl::expected Create(); 30 | static tl::expected, WindowsError> CreatePtr(); 31 | 32 | private: 33 | MessagingWindow(HWND hWnd); 34 | 35 | HWND m_hWnd; 36 | std::unordered_map m_eventHandlerMap; 37 | }; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /real-app/src/Windows/MinimumLatencyAudioClient.cpp: -------------------------------------------------------------------------------- 1 | #include "MinimumLatencyAudioClient.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | using namespace miniant::Windows; 9 | using namespace miniant::Windows::WasapiLatency; 10 | 11 | const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); 12 | const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); 13 | const IID IID_IAudioClient3 = __uuidof(IAudioClient3); 14 | 15 | MinimumLatencyAudioClient::MinimumLatencyAudioClient(MinimumLatencyAudioClient&& other) { 16 | assert(other.m_pAudioClient != nullptr); 17 | assert(other.m_pFormat != nullptr); 18 | 19 | m_pAudioClient = other.m_pAudioClient; 20 | m_pFormat = other.m_pFormat; 21 | 22 | other.m_pAudioClient = nullptr; 23 | other.m_pFormat = nullptr; 24 | } 25 | MinimumLatencyAudioClient::MinimumLatencyAudioClient(void* pAudioClient, void* pFormat) : 26 | m_pAudioClient(pAudioClient), m_pFormat(pFormat) {} 27 | 28 | MinimumLatencyAudioClient::~MinimumLatencyAudioClient() { 29 | Uninitialise(); 30 | } 31 | 32 | MinimumLatencyAudioClient& MinimumLatencyAudioClient::operator= (MinimumLatencyAudioClient&& rhs) { 33 | assert(rhs.m_pAudioClient != nullptr); 34 | assert(rhs.m_pFormat != nullptr); 35 | 36 | Uninitialise(); 37 | m_pAudioClient = rhs.m_pAudioClient; 38 | m_pFormat = rhs.m_pFormat; 39 | 40 | rhs.m_pAudioClient = nullptr; 41 | rhs.m_pFormat = nullptr; 42 | 43 | return *this; 44 | } 45 | 46 | void MinimumLatencyAudioClient::Uninitialise() { 47 | if (m_pAudioClient == nullptr) { 48 | assert(m_pFormat == nullptr); 49 | return; 50 | } 51 | 52 | assert(m_pFormat != nullptr); 53 | 54 | static_cast(m_pAudioClient)->Release(); 55 | m_pAudioClient = nullptr; 56 | 57 | CoTaskMemFree(m_pFormat); 58 | m_pFormat = nullptr; 59 | } 60 | 61 | tl::expected MinimumLatencyAudioClient::GetProperties() { 62 | Properties properties; 63 | HRESULT hr = static_cast(m_pAudioClient)->GetSharedModeEnginePeriod( 64 | static_cast(m_pFormat), 65 | &properties.defaultBufferSize, 66 | &properties.fundamentalBufferSize, 67 | &properties.minimumBufferSize, 68 | &properties.maximumBufferSize); 69 | if (hr != S_OK) { 70 | return tl::make_unexpected(WindowsError()); 71 | } 72 | 73 | properties.sampleRate = static_cast(m_pFormat)->nSamplesPerSec; 74 | properties.bitsPerSample = static_cast(m_pFormat)->wBitsPerSample; 75 | properties.numChannels = static_cast(m_pFormat)->nChannels; 76 | 77 | return properties; 78 | } 79 | 80 | tl::expected MinimumLatencyAudioClient::Start() { 81 | HRESULT hr; 82 | 83 | hr = CoInitialize(NULL); 84 | if (hr != S_OK) { 85 | return tl::make_unexpected(WindowsError()); 86 | } 87 | 88 | IMMDeviceEnumerator* pEnumerator; 89 | hr = CoCreateInstance( 90 | CLSID_MMDeviceEnumerator, 91 | NULL, 92 | CLSCTX_ALL, 93 | IID_IMMDeviceEnumerator, 94 | reinterpret_cast(&pEnumerator)); 95 | if (hr != S_OK) { 96 | return tl::make_unexpected(WindowsError()); 97 | } 98 | 99 | IMMDevice* pDevice; 100 | hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice); 101 | if (hr != S_OK) { 102 | return tl::make_unexpected(WindowsError()); 103 | } 104 | 105 | IAudioClient3* pAudioClient; 106 | hr = pDevice->Activate(IID_IAudioClient3, CLSCTX_ALL, NULL, reinterpret_cast(&pAudioClient)); 107 | if (hr != S_OK) { 108 | return tl::make_unexpected(WindowsError()); 109 | } 110 | 111 | WAVEFORMATEX* pFormat; 112 | hr = pAudioClient->GetMixFormat(&pFormat); 113 | if (hr != S_OK) { 114 | return tl::make_unexpected(WindowsError()); 115 | } 116 | 117 | UINT32 defaultPeriodInFrames; 118 | UINT32 fundamentalPeriodInFrames; 119 | UINT32 minPeriodInFrames; 120 | UINT32 maxPeriodInFrames; 121 | hr = pAudioClient->GetSharedModeEnginePeriod( 122 | pFormat, 123 | &defaultPeriodInFrames, 124 | &fundamentalPeriodInFrames, 125 | &minPeriodInFrames, 126 | &maxPeriodInFrames); 127 | if (hr != S_OK) { 128 | return tl::make_unexpected(WindowsError()); 129 | } 130 | 131 | hr = pAudioClient->InitializeSharedAudioStream( 132 | 0, 133 | minPeriodInFrames, 134 | pFormat, 135 | NULL); 136 | if (hr != S_OK) { 137 | return tl::make_unexpected(WindowsError()); 138 | } 139 | 140 | hr = pAudioClient->Start(); 141 | if (hr != S_OK) { 142 | return tl::make_unexpected(WindowsError()); 143 | } 144 | 145 | return MinimumLatencyAudioClient(pAudioClient, pFormat); 146 | } 147 | -------------------------------------------------------------------------------- /real-app/src/Windows/MinimumLatencyAudioClient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "WindowsError.h" 4 | 5 | #include 6 | 7 | namespace miniant::Windows::WasapiLatency { 8 | 9 | class MinimumLatencyAudioClient { 10 | public: 11 | struct Properties { 12 | uint32_t defaultBufferSize; 13 | uint32_t fundamentalBufferSize; 14 | uint32_t minimumBufferSize; 15 | uint32_t maximumBufferSize; 16 | uint32_t sampleRate; 17 | uint16_t bitsPerSample; 18 | uint16_t numChannels; 19 | }; 20 | 21 | MinimumLatencyAudioClient(MinimumLatencyAudioClient&& other); 22 | ~MinimumLatencyAudioClient(); 23 | 24 | MinimumLatencyAudioClient& operator= (MinimumLatencyAudioClient&& rhs); 25 | 26 | tl::expected GetProperties(); 27 | 28 | static tl::expected Start(); 29 | 30 | private: 31 | void* m_pAudioClient; 32 | void* m_pFormat; 33 | 34 | MinimumLatencyAudioClient(void* pAudioClient, void* pFormat); 35 | 36 | void Uninitialise(); 37 | }; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /real-app/src/Windows/TrayIcon.cpp: -------------------------------------------------------------------------------- 1 | #include "TrayIcon.h" 2 | 3 | #include "GlobalWindowProcedure.h" 4 | 5 | using namespace miniant::Windows; 6 | 7 | TrayIcon::TrayIcon(MessagingWindow& window, HICON hIcon): 8 | m_window(window) { 9 | m_data = {}; 10 | m_data.cbSize = sizeof(m_data); 11 | m_data.uVersion = NOTIFYICON_VERSION_4; 12 | m_data.uFlags = NIF_ICON | NIF_MESSAGE; 13 | m_data.hWnd = window.GetHWindow(); 14 | m_data.hIcon = hIcon; 15 | m_data.uID = 1; 16 | m_data.uCallbackMessage = GlobalWindowProcedure::GetFreeEventId(); 17 | 18 | window.SetEventHandler(m_data.uCallbackMessage, [this](const MessagingWindow& window, WPARAM wParam, LPARAM lParam) { 19 | switch (LOWORD(lParam)) { 20 | case WM_LBUTTONUP: 21 | if (m_lButtonUpHandler) { 22 | m_lButtonUpHandler(*this); 23 | } 24 | 25 | break; 26 | 27 | default: 28 | break; 29 | } 30 | 31 | return std::optional(); 32 | }); 33 | } 34 | 35 | TrayIcon::~TrayIcon() noexcept { 36 | m_window.RemoveEventHandler(m_data.uCallbackMessage); 37 | ::Shell_NotifyIcon(NIM_DELETE, &m_data); 38 | } 39 | 40 | tl::expected TrayIcon::Show() { 41 | if (m_shown) { 42 | return {}; 43 | } 44 | 45 | if (!::Shell_NotifyIcon(NIM_ADD, &m_data)) { 46 | return tl::make_unexpected(WindowsError()); 47 | } 48 | 49 | if (!::Shell_NotifyIcon(NIM_SETVERSION, &m_data)) { 50 | ::Shell_NotifyIcon(NIM_DELETE, &m_data); 51 | return tl::make_unexpected(WindowsError()); 52 | } 53 | 54 | m_shown = true; 55 | return {}; 56 | } 57 | 58 | tl::expected TrayIcon::Hide() { 59 | if (!m_shown) { 60 | return {}; 61 | } 62 | 63 | if (!::Shell_NotifyIcon(NIM_DELETE, &m_data)) { 64 | return tl::make_unexpected(WindowsError()); 65 | } 66 | 67 | m_shown = false; 68 | return {}; 69 | } 70 | 71 | void TrayIcon::SetLButtonUpHandler(TrayEventHandler handler) noexcept { 72 | m_lButtonUpHandler = std::move(handler); 73 | } 74 | -------------------------------------------------------------------------------- /real-app/src/Windows/TrayIcon.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "MessagingWindow.h" 4 | 5 | #include 6 | 7 | namespace miniant::Windows { 8 | 9 | class TrayIcon { 10 | public: 11 | using TrayEventHandler = std::function(TrayIcon&)>; 12 | 13 | TrayIcon(MessagingWindow& window, HICON hIcon); 14 | ~TrayIcon() noexcept; 15 | 16 | tl::expected Show(); 17 | tl::expected Hide(); 18 | 19 | void SetLButtonUpHandler(TrayEventHandler handler) noexcept; 20 | 21 | private: 22 | NOTIFYICONDATA m_data; 23 | MessagingWindow& m_window; 24 | TrayEventHandler m_lButtonUpHandler; 25 | bool m_shown = false; 26 | }; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /real-app/src/Windows/WindowsError.cpp: -------------------------------------------------------------------------------- 1 | #include "WindowsError.h" 2 | 3 | #include 4 | 5 | using namespace miniant::Windows; 6 | 7 | std::string GetErrorMessage() { 8 | DWORD lastError = ::GetLastError(); 9 | return "Last error: " + std::to_string(lastError); 10 | } 11 | 12 | WindowsError::WindowsError(): 13 | ExpectedError(GetErrorMessage()) {} 14 | -------------------------------------------------------------------------------- /real-app/src/Windows/WindowsError.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../ExpectedError.h" 4 | 5 | namespace miniant::Windows { 6 | 7 | class WindowsError : public ExpectedError { 8 | public: 9 | WindowsError(); 10 | 11 | WindowsError(std::string message) noexcept: 12 | ExpectedError(std::move(message)) {} 13 | 14 | WindowsError(const char* message): 15 | ExpectedError(message) {} 16 | }; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /real-app/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "AutoUpdater.h" 2 | #include "OStreamSink.h" 3 | 4 | #include "Windows/Console.h" 5 | #include "Windows/MinimumLatencyAudioClient.h" 6 | #include "Windows/MessagingWindow.h" 7 | #include "Windows/TrayIcon.h" 8 | 9 | #include "../res/resource.h" 10 | 11 | #include 12 | 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | using namespace miniant::AutoUpdater; 20 | using namespace miniant::Spdlog; 21 | using namespace miniant::Windows; 22 | using namespace miniant::Windows::WasapiLatency; 23 | 24 | constexpr Version APP_VERSION(0, 2, 0); 25 | constexpr TCHAR COMMAND_LINE_OPTION_TRAY[] = TEXT("--tray"); 26 | 27 | void WaitForAnyKey(const std::string& message) { 28 | while (_kbhit()) { 29 | _getch(); 30 | } 31 | 32 | spdlog::get("app_out")->info(message); 33 | _getch(); 34 | } 35 | 36 | void DisplayExitMessage(bool success) { 37 | if (success) { 38 | WaitForAnyKey("\nPress any key to disable and exit . . ."); 39 | } else { 40 | WaitForAnyKey("\nPress any key to exit . . ."); 41 | } 42 | } 43 | 44 | std::string ToLower(const std::string& string) { 45 | std::string result; 46 | for (const auto& c : string) { 47 | result.append(1, std::tolower(c, std::locale())); 48 | } 49 | 50 | return result; 51 | } 52 | 53 | int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) { 54 | auto oss = std::make_shared(); 55 | auto sink = std::make_shared(oss, true); 56 | auto app_out = std::make_shared("app_out", sink); 57 | app_out->set_pattern("%v"); 58 | spdlog::register_logger(app_out); 59 | 60 | auto console = std::make_shared([=] { 61 | std::lock_guard lock(sink->GetMutex()); 62 | std::cout << oss->str(); 63 | std::cout.flush(); 64 | oss->set_rdbuf(std::cout.rdbuf()); 65 | }); 66 | 67 | std::unique_ptr window; 68 | std::unique_ptr trayIcon; 69 | 70 | std::wstring commandLine(pCmdLine); 71 | bool success = true; 72 | 73 | if (commandLine == COMMAND_LINE_OPTION_TRAY) { 74 | tl::expected windowPtrResult = MessagingWindow::CreatePtr(); 75 | if (!windowPtrResult) { 76 | #pragma push_macro("GetMessage") 77 | #undef GetMessage 78 | app_out->error("Error: {}", windowPtrResult.error().GetMessage()); 79 | return 1; 80 | } 81 | 82 | window = std::move(*windowPtrResult); 83 | HICON hIcon = ::LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1)); 84 | trayIcon = std::make_unique(*window, hIcon); 85 | trayIcon->SetLButtonUpHandler([=, &success](TrayIcon& trayIcon) { 86 | trayIcon.Hide(); 87 | console->Open(); 88 | 89 | DisplayExitMessage(success); 90 | 91 | console->Close(); 92 | return std::optional(); 93 | }); 94 | trayIcon->Show(); 95 | } else { 96 | console->Open(); 97 | } 98 | 99 | app_out->info("REAL - REduce Audio Latency {}, mini)(ant, 2018-2019", APP_VERSION.ToString()); 100 | app_out->info("Project: https://github.com/miniant-git/REAL\n"); 101 | 102 | auto audioClient = MinimumLatencyAudioClient::Start(); 103 | if (!audioClient) { 104 | success = false; 105 | app_out->info("ERROR: Could not enable low-latency mode.\n"); 106 | } else { 107 | app_out->info("Minimum audio latency enabled on the DEFAULT playback device!\n"); 108 | auto properties = audioClient->GetProperties(); 109 | if (properties) { 110 | app_out->info( 111 | "Device properties:\n Sample rate{:.>16} Hz\n Buffer size (min){:.>10} samples ({} ms) [current]\n Buffer size (max){:.>10} samples ({} ms)\n Buffer size (default){:.>6} samples ({} ms)\n", 112 | properties->sampleRate, 113 | properties->minimumBufferSize, 1000.0f * properties->minimumBufferSize / properties->sampleRate, 114 | properties->maximumBufferSize, 1000.0f * properties->maximumBufferSize / properties->sampleRate, 115 | properties->defaultBufferSize, 1000.0f * properties->defaultBufferSize / properties->sampleRate); 116 | } 117 | } 118 | 119 | AutoUpdater updater; 120 | tl::expected cleanupResult = updater.CleanupPreviousSetup(); 121 | if (!cleanupResult) { 122 | app_out->info("Error: {}", cleanupResult.error().GetMessage()); 123 | } 124 | 125 | if (auto notes = updater.IsAppSuperseded(); notes) { 126 | if (trayIcon != nullptr) { 127 | trayIcon->Hide(); 128 | } 129 | 130 | console->Open(); 131 | 132 | app_out->info(*notes); 133 | 134 | DisplayExitMessage(success); 135 | console->Close(); 136 | return 0; 137 | } 138 | 139 | app_out->info("Checking for updates..."); 140 | tl::expected info = updater.GetUpdateInfo(); 141 | if (!info) { 142 | app_out->info("Error: {}", info.error().GetMessage()); 143 | app_out->info("Update failed!"); 144 | } else if (info->version > APP_VERSION) { 145 | if (trayIcon != nullptr) { 146 | trayIcon->Hide(); 147 | } 148 | 149 | console->Open(); 150 | 151 | app_out->info("A new update is available!"); 152 | if (info->releaseNotes) { 153 | app_out->info(*info->releaseNotes); 154 | } 155 | 156 | std::cout << "Do you want to update to " << info->version.ToString() << "? [y/N] : "; 157 | char line[5]; 158 | std::cin.getline(line, 5); 159 | std::string prompt(ToLower(line)); 160 | if (prompt == "y" || prompt == "yes") { 161 | auto status = updater.ApplyUpdate(*info); 162 | if (status) { 163 | app_out->info("Updated successfully! Restart the application to apply changes."); 164 | } else { 165 | app_out->info("Update failed."); 166 | app_out->info("Error: {}", status.error().GetMessage()); 167 | } 168 | 169 | } else { 170 | app_out->info("No: Keeping the current version."); 171 | } 172 | 173 | DisplayExitMessage(success); 174 | return 2; 175 | } else { 176 | app_out->info("The application is up-to-date."); 177 | } 178 | 179 | #pragma pop_macro("GetMessage") 180 | if (commandLine == COMMAND_LINE_OPTION_TRAY) { 181 | MSG msg; 182 | while (::GetMessage(&msg, NULL, 0, 0) > 0) { 183 | ::TranslateMessage(&msg); 184 | ::DispatchMessage(&msg); 185 | } 186 | } else { 187 | DisplayExitMessage(success); 188 | } 189 | 190 | return 0; 191 | } 192 | --------------------------------------------------------------------------------