├── .gitattributes ├── .gitignore ├── ControllerOverlay.sln └── ControllerOverlay ├── ControllerOverlay.cpp ├── ControllerOverlay.h ├── ControllerOverlay.vcxproj ├── ControllerOverlay.vcxproj.filters └── imgui ├── imconfig.h ├── imgui.cpp ├── imgui.h ├── imgui_additions.cpp ├── imgui_additions.h ├── imgui_demo.cpp ├── imgui_draw.cpp ├── imgui_impl_dx11.cpp ├── imgui_impl_dx11.h ├── imgui_impl_win32.cpp ├── imgui_impl_win32.h ├── imgui_internal.h ├── imgui_rangeslider.cpp ├── imgui_rangeslider.h ├── imgui_searchablecombo.cpp ├── imgui_searchablecombo.h ├── imgui_timeline.cpp ├── imgui_timeline.h ├── imgui_widgets.cpp ├── imguivariouscontrols.cpp ├── imguivariouscontrols.h ├── imstb_rectpack.h ├── imstb_textedit.h └── imstb_truetype.h /.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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | [Aa][Rr][Mm]/ 24 | [Aa][Rr][Mm]64/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015/2017 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # Visual Studio 2017 auto generated files 36 | Generated\ Files/ 37 | 38 | # MSTest test Results 39 | [Tt]est[Rr]esult*/ 40 | [Bb]uild[Ll]og.* 41 | 42 | # NUNIT 43 | *.VisualState.xml 44 | TestResult.xml 45 | 46 | # Build Results of an ATL Project 47 | [Dd]ebugPS/ 48 | [Rr]eleasePS/ 49 | dlldata.c 50 | 51 | # Benchmark Results 52 | BenchmarkDotNet.Artifacts/ 53 | 54 | # .NET Core 55 | project.lock.json 56 | project.fragment.lock.json 57 | artifacts/ 58 | 59 | # StyleCop 60 | StyleCopReport.xml 61 | 62 | # Files built by Visual Studio 63 | *_i.c 64 | *_p.c 65 | *_h.h 66 | *.ilk 67 | *.meta 68 | *.obj 69 | *.iobj 70 | *.pch 71 | *.pdb 72 | *.ipdb 73 | *.pgc 74 | *.pgd 75 | *.rsp 76 | *.sbr 77 | *.tlb 78 | *.tli 79 | *.tlh 80 | *.tmp 81 | *.tmp_proj 82 | *_wpftmp.csproj 83 | *.log 84 | *.vspscc 85 | *.vssscc 86 | .builds 87 | *.pidb 88 | *.svclog 89 | *.scc 90 | 91 | # Chutzpah Test files 92 | _Chutzpah* 93 | 94 | # Visual C++ cache files 95 | ipch/ 96 | *.aps 97 | *.ncb 98 | *.opendb 99 | *.opensdf 100 | *.sdf 101 | *.cachefile 102 | *.VC.db 103 | *.VC.VC.opendb 104 | 105 | # Visual Studio profiler 106 | *.psess 107 | *.vsp 108 | *.vspx 109 | *.sap 110 | 111 | # Visual Studio Trace Files 112 | *.e2e 113 | 114 | # TFS 2012 Local Workspace 115 | $tf/ 116 | 117 | # Guidance Automation Toolkit 118 | *.gpState 119 | 120 | # ReSharper is a .NET coding add-in 121 | _ReSharper*/ 122 | *.[Rr]e[Ss]harper 123 | *.DotSettings.user 124 | 125 | # JustCode is a .NET coding add-in 126 | .JustCode 127 | 128 | # TeamCity is a build add-in 129 | _TeamCity* 130 | 131 | # DotCover is a Code Coverage Tool 132 | *.dotCover 133 | 134 | # AxoCover is a Code Coverage Tool 135 | .axoCover/* 136 | !.axoCover/settings.json 137 | 138 | # Visual Studio code coverage results 139 | *.coverage 140 | *.coveragexml 141 | 142 | # NCrunch 143 | _NCrunch_* 144 | .*crunch*.local.xml 145 | nCrunchTemp_* 146 | 147 | # MightyMoose 148 | *.mm.* 149 | AutoTest.Net/ 150 | 151 | # Web workbench (sass) 152 | .sass-cache/ 153 | 154 | # Installshield output folder 155 | [Ee]xpress/ 156 | 157 | # DocProject is a documentation generator add-in 158 | DocProject/buildhelp/ 159 | DocProject/Help/*.HxT 160 | DocProject/Help/*.HxC 161 | DocProject/Help/*.hhc 162 | DocProject/Help/*.hhk 163 | DocProject/Help/*.hhp 164 | DocProject/Help/Html2 165 | DocProject/Help/html 166 | 167 | # Click-Once directory 168 | publish/ 169 | 170 | # Publish Web Output 171 | *.[Pp]ublish.xml 172 | *.azurePubxml 173 | # Note: Comment the next line if you want to checkin your web deploy settings, 174 | # but database connection strings (with potential passwords) will be unencrypted 175 | *.pubxml 176 | *.publishproj 177 | 178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 179 | # checkin your Azure Web App publish settings, but sensitive information contained 180 | # in these scripts will be unencrypted 181 | PublishScripts/ 182 | 183 | # NuGet Packages 184 | *.nupkg 185 | # The packages folder can be ignored because of Package Restore 186 | **/[Pp]ackages/* 187 | # except build/, which is used as an MSBuild target. 188 | !**/[Pp]ackages/build/ 189 | # Uncomment if necessary however generally it will be regenerated when needed 190 | #!**/[Pp]ackages/repositories.config 191 | # NuGet v3's project.json files produces more ignorable files 192 | *.nuget.props 193 | *.nuget.targets 194 | 195 | # Microsoft Azure Build Output 196 | csx/ 197 | *.build.csdef 198 | 199 | # Microsoft Azure Emulator 200 | ecf/ 201 | rcf/ 202 | 203 | # Windows Store app package directories and files 204 | AppPackages/ 205 | BundleArtifacts/ 206 | Package.StoreAssociation.xml 207 | _pkginfo.txt 208 | *.appx 209 | 210 | # Visual Studio cache files 211 | # files ending in .cache can be ignored 212 | *.[Cc]ache 213 | # but keep track of directories ending in .cache 214 | !?*.[Cc]ache/ 215 | 216 | # Others 217 | ClientBin/ 218 | ~$* 219 | *~ 220 | *.dbmdl 221 | *.dbproj.schemaview 222 | *.jfm 223 | *.pfx 224 | *.publishsettings 225 | orleans.codegen.cs 226 | 227 | # Including strong name files can present a security risk 228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 229 | #*.snk 230 | 231 | # Since there are multiple workflows, uncomment next line to ignore bower_components 232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 233 | #bower_components/ 234 | 235 | # RIA/Silverlight projects 236 | Generated_Code/ 237 | 238 | # Backup & report files from converting an old project file 239 | # to a newer Visual Studio version. Backup files are not needed, 240 | # because we have git ;-) 241 | _UpgradeReport_Files/ 242 | Backup*/ 243 | UpgradeLog*.XML 244 | UpgradeLog*.htm 245 | ServiceFabricBackup/ 246 | *.rptproj.bak 247 | 248 | # SQL Server files 249 | *.mdf 250 | *.ldf 251 | *.ndf 252 | 253 | # Business Intelligence projects 254 | *.rdl.data 255 | *.bim.layout 256 | *.bim_*.settings 257 | *.rptproj.rsuser 258 | *- Backup*.rdl 259 | 260 | # Microsoft Fakes 261 | FakesAssemblies/ 262 | 263 | # GhostDoc plugin setting file 264 | *.GhostDoc.xml 265 | 266 | # Node.js Tools for Visual Studio 267 | .ntvs_analysis.dat 268 | node_modules/ 269 | 270 | # Visual Studio 6 build log 271 | *.plg 272 | 273 | # Visual Studio 6 workspace options file 274 | *.opt 275 | 276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 277 | *.vbw 278 | 279 | # Visual Studio LightSwitch build output 280 | **/*.HTMLClient/GeneratedArtifacts 281 | **/*.DesktopClient/GeneratedArtifacts 282 | **/*.DesktopClient/ModelManifest.xml 283 | **/*.Server/GeneratedArtifacts 284 | **/*.Server/ModelManifest.xml 285 | _Pvt_Extensions 286 | 287 | # Paket dependency manager 288 | .paket/paket.exe 289 | paket-files/ 290 | 291 | # FAKE - F# Make 292 | .fake/ 293 | 294 | # JetBrains Rider 295 | .idea/ 296 | *.sln.iml 297 | 298 | # CodeRush personal settings 299 | .cr/personal 300 | 301 | # Python Tools for Visual Studio (PTVS) 302 | __pycache__/ 303 | *.pyc 304 | 305 | # Cake - Uncomment if you are using it 306 | # tools/** 307 | # !tools/packages.config 308 | 309 | # Tabs Studio 310 | *.tss 311 | 312 | # Telerik's JustMock configuration file 313 | *.jmconfig 314 | 315 | # BizTalk build output 316 | *.btp.cs 317 | *.btm.cs 318 | *.odx.cs 319 | *.xsd.cs 320 | 321 | # OpenCover UI analysis results 322 | OpenCover/ 323 | 324 | # Azure Stream Analytics local run output 325 | ASALocalRun/ 326 | 327 | # MSBuild Binary and Structured Log 328 | *.binlog 329 | 330 | # NVidia Nsight GPU debugger configuration file 331 | *.nvuser 332 | 333 | # MFractors (Xamarin productivity tool) working folder 334 | .mfractor/ 335 | 336 | # Local History for Visual Studio 337 | .localhistory/ 338 | 339 | # BeatPulse healthcheck temp database 340 | healthchecksdb -------------------------------------------------------------------------------- /ControllerOverlay.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29409.12 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ControllerOverlay", "ControllerOverlay\ControllerOverlay.vcxproj", "{48C21FDA-5BF0-47DD-B4F6-16303E299097}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {48C21FDA-5BF0-47DD-B4F6-16303E299097}.Debug|x64.ActiveCfg = Debug|x64 17 | {48C21FDA-5BF0-47DD-B4F6-16303E299097}.Debug|x64.Build.0 = Debug|x64 18 | {48C21FDA-5BF0-47DD-B4F6-16303E299097}.Debug|x86.ActiveCfg = Debug|Win32 19 | {48C21FDA-5BF0-47DD-B4F6-16303E299097}.Debug|x86.Build.0 = Debug|Win32 20 | {48C21FDA-5BF0-47DD-B4F6-16303E299097}.Release|x64.ActiveCfg = Release|x64 21 | {48C21FDA-5BF0-47DD-B4F6-16303E299097}.Release|x64.Build.0 = Release|x64 22 | {48C21FDA-5BF0-47DD-B4F6-16303E299097}.Release|x86.ActiveCfg = Release|x64 23 | {48C21FDA-5BF0-47DD-B4F6-16303E299097}.Release|x86.Build.0 = Release|x64 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {06E3D537-3621-44C5-BC3F-4CB316656344} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /ControllerOverlay/ControllerOverlay.cpp: -------------------------------------------------------------------------------- 1 | #include "ControllerOverlay.h" 2 | 3 | BAKKESMOD_PLUGIN(ControllerOverlay, "Controller Overlay", "1.5", 0) 4 | 5 | /* 6 | https://docs.unrealengine.com/udk/Three/KeyBinds.html 7 | 8 | XboxTypeS_LeftX - Left Analogue Stick X Axis 9 | XboxTypeS_RightX - Left Analogue Stick Y Axis 10 | XboxTypeS_LeftY - Right Analogue Stick X Axis 11 | XboxTypeS_RightY - Right Analogue Stick Y Axis 12 | XboxTypeS_DPad_Left - DPad Left 13 | XboxTypeS_DPad_Right - DPad Right 14 | XboxTypeS_DPad_Up - DPad Up 15 | XboxTypeS_DPad_Down - DPad Down 16 | XboxTypeS_A - A Button 17 | XboxTypeS_B - B Button 18 | XboxTypeS_Y - Y Button 19 | XboxTypeS_X - X Button 20 | XboxTypeS_Start - Start Button 21 | XboxTypeS_Back - Back Button 22 | XboxTypeS_LeftShoulder - Left Shoulder 23 | XboxTypeS_RightShoulder - Right Shoulder 24 | XboxTypeS_LeftTrigger - Left Trigger 25 | XboxTypeS_RightTrigger - Right Trigger 26 | XboxTypeS_LeftThumbStick - Left Analogue Stick Button 27 | XboxTypeS_RightThumbStick - Right Analogue Stick Button 28 | 29 | Gamepad_LeftStick_Left - Left Analogue Stick Left 30 | Gamepad_LeftStick_Right - Left Analogue Stick Right 31 | Gamepad_LeftStick_Up - Left Analogue Stick Up 32 | Gamepad_LeftStick_Down - Left Analogue Stick Down 33 | Gamepad_RightStick_Left - Right Analogue Stick Left 34 | Gamepad_RightStick_Right - Right Analogue Stick Right 35 | Gamepad_RightStick_Up - Right Analogue Stick Up 36 | Gamepad_RightStick_Down - Right Analogue Stick Down 37 | */ 38 | 39 | void ControllerOverlay::onLoad() 40 | { 41 | gameWrapper->SetTimeout([this](GameWrapper* gameWrapper) { 42 | cvarManager->executeCommand("togglemenu " + GetMenuName()); 43 | }, 1); 44 | 45 | cvarManager->registerCvar("controllerTitleBar", "1").addOnValueChanged([this](std::string old, CVarWrapper now) { 46 | titleBar = (now.getStringValue() == "1"); 47 | 48 | writeCfg(); 49 | }); 50 | 51 | cvarManager->registerCvar("controllerTransparency", "1.0").addOnValueChanged([this](std::string old, CVarWrapper now) { 52 | transparency = now.getFloatValue(); 53 | 54 | writeCfg(); 55 | }); 56 | 57 | cvarManager->registerCvar("controllerType", "xbox").addOnValueChanged([this](std::string old, CVarWrapper now) { 58 | if (now.getStringValue() == "ps4") { 59 | type = 1; 60 | 61 | inputs["XboxTypeS_A"] = { 0, false, BLUE, "-" }; 62 | inputs["XboxTypeS_B"] = { 0, false, RED, "-" }; 63 | inputs["XboxTypeS_X"] = { 0, false, PURPLE, "-" }; 64 | inputs["XboxTypeS_Y"] = { 0, false, DARKGREEN, "-" }; 65 | inputs["XboxTypeS_LeftShoulder"] = { 0, false, WHITE, "L1" }; 66 | inputs["XboxTypeS_RightShoulder"] = { 0, false, WHITE, "R1" }; 67 | inputs["XboxTypeS_LeftTrigger"] = { 0, false, WHITE, "L2" }; 68 | inputs["XboxTypeS_RightTrigger"] = { 0, false, WHITE, "R2" }; 69 | inputs["XboxTypeS_LeftThumbStick"] = { 0, false, GREY, "L3" }; 70 | } 71 | 72 | else { 73 | type = 0; 74 | 75 | inputs["XboxTypeS_A"] = { 0, false, GREEN, "A" }; 76 | inputs["XboxTypeS_B"] = { 0, false, RED, "B" }; 77 | inputs["XboxTypeS_X"] = { 0, false, BLUE, "X" }; 78 | inputs["XboxTypeS_Y"] = { 0, false, YELLOW, "Y" }; 79 | inputs["XboxTypeS_LeftShoulder"] = { 0, false, WHITE, "LB" }; 80 | inputs["XboxTypeS_RightShoulder"] = { 0, false, WHITE, "RB" }; 81 | inputs["XboxTypeS_LeftTrigger"] = { 0, false, WHITE, "LT" }; 82 | inputs["XboxTypeS_RightTrigger"] = { 0, false, WHITE, "RT" }; 83 | inputs["XboxTypeS_LeftThumbStick"] = { 0, false, GREY, "LS" }; 84 | } 85 | 86 | for (const std::pair& input : inputs) { 87 | cvarManager->registerCvar(input.first, input.first).addOnValueChanged([this](std::string old, CVarWrapper now) { 88 | inputs[now.getStringValue()].index = gameWrapper->GetFNameIndexByString(now.getStringValue()); 89 | }); 90 | 91 | cvarManager->getCvar(input.first).notify(); 92 | } 93 | 94 | writeCfg(); 95 | }); 96 | 97 | cvarManager->registerCvar("controllerSize", "0").addOnValueChanged([this](std::string old, CVarWrapper now) { 98 | if (now.getIntValue() == 0 || now.getIntValue() == 1) { 99 | size = now.getIntValue(); 100 | } 101 | 102 | else { 103 | size = 0; 104 | } 105 | 106 | writeCfg(); 107 | }); 108 | 109 | if (std::ifstream(configurationFilePath)) { 110 | cvarManager->loadCfg(configurationFilePath); 111 | } 112 | 113 | else { 114 | cvarManager->getCvar("controllerType").notify(); 115 | } 116 | 117 | gameWrapper->HookEvent("Function Engine.GameViewportClient.Tick", bind(&ControllerOverlay::onTick, this, std::placeholders::_1)); 118 | } 119 | 120 | void ControllerOverlay::onUnload() 121 | { 122 | writeCfg(); 123 | 124 | if (renderControllerOverlay) { 125 | cvarManager->executeCommand("togglemenu " + GetMenuName()); 126 | } 127 | } 128 | 129 | void ControllerOverlay::writeCfg() 130 | { 131 | std::ofstream configurationFile; 132 | 133 | configurationFile.open(configurationFilePath); 134 | 135 | configurationFile << "controllerTitleBar \"" + std::to_string(titleBar) + "\""; 136 | configurationFile << "\n"; 137 | configurationFile << "controllerTransparency \"" + std::to_string(transparency) + "\""; 138 | configurationFile << "\n"; 139 | 140 | if (type == 1) { 141 | configurationFile << "controllerType \"ps4\""; 142 | } 143 | 144 | else { 145 | configurationFile << "controllerType \"xbox\""; 146 | } 147 | 148 | configurationFile << "\n"; 149 | configurationFile << "controllerSize \"" + std::to_string(size) + "\""; 150 | 151 | configurationFile.close(); 152 | } 153 | 154 | void ControllerOverlay::onTick(std::string eventName) 155 | { 156 | if (!gameWrapper->IsInCustomTraining()) { 157 | if (gameWrapper->IsInGame() || gameWrapper->IsInOnlineGame()) { 158 | for (const std::pair& input : inputs) { 159 | if (input.second.index > 0) { 160 | inputs[input.first].pressed = gameWrapper->IsKeyPressed(input.second.index); 161 | } 162 | } 163 | 164 | CarWrapper car = gameWrapper->GetLocalCar(); 165 | 166 | if (!car.IsNull()) { 167 | controllerInput = car.GetInput(); 168 | } 169 | 170 | else { 171 | controllerInput.Steer = 0; 172 | controllerInput.Pitch = 0; 173 | } 174 | } 175 | } 176 | } 177 | 178 | void ControllerOverlay::Render() 179 | { 180 | if (!renderControllerOverlay) { 181 | cvarManager->executeCommand("togglemenu " + GetMenuName()); 182 | 183 | return; 184 | } 185 | 186 | if (!gameWrapper->IsInCustomTraining()) { 187 | if (gameWrapper->IsInOnlineGame()) { 188 | ServerWrapper server = gameWrapper->GetOnlineGame(); 189 | 190 | if (!server.IsNull()) { 191 | if (!server.GetbMatchEnded()) { 192 | ControllerOverlay::RenderImGui(); 193 | } 194 | } 195 | } 196 | 197 | else if (gameWrapper->IsInGame()) { 198 | ServerWrapper server = gameWrapper->GetGameEventAsServer(); 199 | 200 | if (!server.IsNull()) { 201 | if (!server.GetbMatchEnded()) { 202 | ControllerOverlay::RenderImGui(); 203 | } 204 | } 205 | } 206 | } 207 | } 208 | 209 | void ControllerOverlay::RenderImGui() 210 | { 211 | if (renderSettings) { 212 | ImGui::SetNextWindowPos(ImVec2(128, 256), ImGuiCond_FirstUseEver); 213 | ImGui::SetNextWindowSize(ImVec2(0, 0)); 214 | 215 | ImGui::Begin((GetMenuTitle() + " - Settings").c_str(), &renderSettings, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize); 216 | 217 | ImGui::Checkbox("Titlebar", &titleBar); 218 | ImGui::SliderFloat("Transparency", &transparency, 0.f, 1.f, "%.2f"); 219 | const char* types[] = { "Xbox", "PS4" }; 220 | ImGui::Combo("Type", &type, types, IM_ARRAYSIZE(types)); 221 | const char* sizes[] = { "Small @1x", "Large @2x" }; 222 | ImGui::Combo("Size", &size, sizes, IM_ARRAYSIZE(sizes)); 223 | ImGui::End(); 224 | } 225 | 226 | float scale = (size == 1 ? 2.0f : 1.0f); 227 | 228 | ImGui::SetNextWindowBgAlpha(transparency); 229 | 230 | ImGui::SetNextWindowPos(ImVec2(128, 128), ImGuiCond_FirstUseEver); 231 | 232 | float windowHeight = 156 * scale; 233 | 234 | if (!titleBar) { 235 | windowHeight -= ImGui::GetFrameHeight(); 236 | } 237 | 238 | ImVec2 windowSize = ImVec2(216 * scale, windowHeight); 239 | 240 | if (size == 1) { 241 | windowSize.x -= 16; 242 | windowSize.y -= 32; 243 | } 244 | 245 | ImGui::SetNextWindowSize(windowSize); 246 | 247 | ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize; 248 | 249 | if (!titleBar) { 250 | windowFlags = windowFlags | ImGuiWindowFlags_NoTitleBar; 251 | } 252 | 253 | ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0); 254 | 255 | ImGui::Begin(GetMenuTitle().c_str(), &renderControllerOverlay, windowFlags); 256 | 257 | ImVec2 cursorPosition = ImGui::GetCursorPos(); 258 | 259 | if (size == 1) { 260 | ImGuiIO io = ImGui::GetIO(); 261 | 262 | if (io.Fonts->Fonts.size() >= 1) { 263 | ImFont* font = io.Fonts->Fonts[1]; 264 | 265 | ImGui::PushFont(font); 266 | } 267 | } 268 | 269 | ImDrawList* drawList = ImGui::GetWindowDrawList(); 270 | 271 | ImVec2 p = ImGui::GetCursorScreenPos(); 272 | 273 | p.x += 12 * scale; 274 | 275 | float buttonWidth = 48 * scale, buttonHeight = 16 * scale; 276 | 277 | ImVec2 buttonLBPosition = ImVec2(p.x, p.y); 278 | ImVec2 buttonRBPosition = ImVec2(p.x + 128 * scale, p.y); 279 | 280 | if (inputs["XboxTypeS_LeftTrigger"].pressed) { 281 | drawList->AddRectFilled(buttonLBPosition, ImVec2(buttonLBPosition.x + buttonWidth, buttonLBPosition.y + buttonHeight), WHITE, 8, ImDrawCornerFlags_TopLeft); 282 | drawList->AddText(ImVec2(buttonLBPosition.x + 18 * scale, buttonLBPosition.y + 1 * scale), BLACK, inputs["XboxTypeS_LeftTrigger"].name.c_str()); 283 | } 284 | 285 | else { 286 | drawList->AddRect(buttonLBPosition, ImVec2(buttonLBPosition.x + buttonWidth, buttonLBPosition.y + buttonHeight), WHITE, 8, ImDrawCornerFlags_TopLeft, 2 * scale); 287 | drawList->AddText(ImVec2(buttonLBPosition.x + 18 * scale, buttonLBPosition.y + 1 * scale), WHITE, inputs["XboxTypeS_LeftTrigger"].name.c_str()); 288 | } 289 | 290 | if (inputs["XboxTypeS_RightTrigger"].pressed) { 291 | drawList->AddRectFilled(buttonRBPosition, ImVec2(buttonRBPosition.x + buttonWidth, buttonRBPosition.y + buttonHeight), WHITE, 8, ImDrawCornerFlags_TopRight); 292 | drawList->AddText(ImVec2(buttonRBPosition.x + 18 * scale, buttonRBPosition.y + 1 * scale), BLACK, inputs["XboxTypeS_RightTrigger"].name.c_str()); 293 | } 294 | 295 | else { 296 | drawList->AddRect(buttonRBPosition, ImVec2(buttonRBPosition.x + buttonWidth, buttonRBPosition.y + buttonHeight), WHITE, 8, ImDrawCornerFlags_TopRight, 2 * scale); 297 | drawList->AddText(ImVec2(buttonRBPosition.x + 18 * scale, buttonRBPosition.y + 1 * scale), WHITE, inputs["XboxTypeS_RightTrigger"].name.c_str()); 298 | } 299 | 300 | p.y += buttonHeight + 4 * scale; 301 | 302 | ImVec2 buttonLTPosition = ImVec2(p.x, p.y); 303 | ImVec2 buttonRTPosition = ImVec2(p.x + 128 * scale, p.y); 304 | 305 | if (inputs["XboxTypeS_LeftShoulder"].pressed) { 306 | drawList->AddRectFilled(buttonLTPosition, ImVec2(buttonLTPosition.x + buttonWidth, buttonLTPosition.y + buttonHeight), WHITE); 307 | drawList->AddText(ImVec2(buttonLTPosition.x + 18 * scale, buttonLTPosition.y + 1 * scale), BLACK, inputs["XboxTypeS_LeftShoulder"].name.c_str()); 308 | } 309 | 310 | else { 311 | drawList->AddRect(buttonLTPosition, ImVec2(buttonLTPosition.x + buttonWidth, buttonLTPosition.y + buttonHeight), WHITE, 0, 0, 2 * scale); 312 | drawList->AddText(ImVec2(buttonLTPosition.x + 18 * scale, buttonLTPosition.y + 1 * scale), WHITE, inputs["XboxTypeS_LeftShoulder"].name.c_str()); 313 | } 314 | 315 | if (inputs["XboxTypeS_RightShoulder"].pressed) { 316 | drawList->AddRectFilled(buttonRTPosition, ImVec2(buttonRTPosition.x + buttonWidth, buttonRTPosition.y + buttonHeight), WHITE); 317 | drawList->AddText(ImVec2(buttonRTPosition.x + 18 * scale, buttonRTPosition.y + 1 * scale), BLACK, inputs["XboxTypeS_RightShoulder"].name.c_str()); 318 | } 319 | 320 | else { 321 | drawList->AddRect(buttonRTPosition, ImVec2(buttonRTPosition.x + buttonWidth, buttonRTPosition.y + buttonHeight), WHITE, 0, 0, 2 * scale); 322 | drawList->AddText(ImVec2(buttonRTPosition.x + 18 * scale, buttonRTPosition.y + 1 * scale), WHITE, inputs["XboxTypeS_RightShoulder"].name.c_str()); 323 | } 324 | 325 | p.y += buttonHeight + 16 * scale; 326 | 327 | p.x -= 8 * scale; 328 | 329 | float leftStickRadius = 32 * scale; 330 | ImVec2 leftStickCenter = ImVec2(p.x + leftStickRadius, p.y + leftStickRadius); 331 | 332 | drawList->AddCircle(leftStickCenter, 24 * scale, WHITE, 32, 2 * scale); 333 | 334 | drawList->AddCircleFilled(ImVec2(leftStickCenter.x + (controllerInput.Steer * 8 * scale), leftStickCenter.y + (controllerInput.Pitch * 8 * scale)), 20 * scale, (inputs["XboxTypeS_LeftThumbStick"].pressed ? GREY : WHITE), 32); 335 | drawList->AddCircleFilled(ImVec2(leftStickCenter.x + (controllerInput.Steer * 8 * scale), leftStickCenter.y + (controllerInput.Pitch * 8 * scale)), 16 * scale, (inputs["XboxTypeS_LeftThumbStick"].pressed ? DARKGREY : GREY), 32); 336 | 337 | float buttonRadius = 12 * scale; 338 | ImVec2 buttonsCenter = ImVec2(leftStickCenter.x + 128 * scale, leftStickCenter.y); 339 | 340 | std::map buttonPositions; 341 | std::map buttonTextPositions; 342 | 343 | if (type == 0) { 344 | buttonPositions["XboxTypeS_A"] = ImVec2(buttonsCenter.x, buttonsCenter.y + buttonRadius * 2); 345 | buttonPositions["XboxTypeS_B"] = ImVec2(buttonsCenter.x + buttonRadius * 2, buttonsCenter.y); 346 | buttonPositions["XboxTypeS_X"] = ImVec2(buttonsCenter.x - buttonRadius * 2, buttonsCenter.y); 347 | buttonPositions["XboxTypeS_Y"] = ImVec2(buttonsCenter.x, buttonsCenter.y - buttonRadius * 2); 348 | 349 | buttonTextPositions["XboxTypeS_A"] = ImVec2(3 * scale + buttonPositions["XboxTypeS_A"].x - buttonRadius * 0.5f, buttonPositions["XboxTypeS_A"].y - buttonRadius * 0.5f - 1); 350 | buttonTextPositions["XboxTypeS_B"] = ImVec2(3 * scale + buttonPositions["XboxTypeS_B"].x - buttonRadius * 0.5f, buttonPositions["XboxTypeS_B"].y - buttonRadius * 0.5f - 1); 351 | buttonTextPositions["XboxTypeS_X"] = ImVec2(3 * scale + buttonPositions["XboxTypeS_X"].x - buttonRadius * 0.5f, buttonPositions["XboxTypeS_X"].y - buttonRadius * 0.5f - 1); 352 | buttonTextPositions["XboxTypeS_Y"] = ImVec2(3 * scale + buttonPositions["XboxTypeS_Y"].x - buttonRadius * 0.5f, buttonPositions["XboxTypeS_Y"].y - buttonRadius * 0.5f - 1); 353 | 354 | for (std::pair buttonPosition : buttonPositions) { 355 | if (inputs[buttonPosition.first].pressed) { 356 | drawList->AddCircleFilled(buttonPosition.second, buttonRadius, inputs[buttonPosition.first].color, 32); 357 | drawList->AddText(buttonTextPositions[buttonPosition.first], BLACK, inputs[buttonPosition.first].name.c_str()); 358 | } 359 | 360 | else { 361 | drawList->AddCircle(buttonPosition.second, buttonRadius, inputs[buttonPosition.first].color, 32, 2 * scale); 362 | drawList->AddText(buttonTextPositions[buttonPosition.first], inputs[buttonPosition.first].color, inputs[buttonPosition.first].name.c_str()); 363 | } 364 | } 365 | } 366 | 367 | else if (type == 1) { 368 | buttonPositions["XboxTypeS_A"] = ImVec2(buttonsCenter.x, buttonsCenter.y + buttonRadius * 2); 369 | buttonPositions["XboxTypeS_B"] = ImVec2(buttonsCenter.x + buttonRadius * 2, buttonsCenter.y); 370 | buttonPositions["XboxTypeS_X"] = ImVec2(buttonsCenter.x - buttonRadius * 2, buttonsCenter.y); 371 | buttonPositions["XboxTypeS_Y"] = ImVec2(buttonsCenter.x, buttonsCenter.y - buttonRadius * 2); 372 | 373 | for (std::pair buttonPosition : buttonPositions) { 374 | if (inputs[buttonPosition.first].pressed) { 375 | drawList->AddCircleFilled(buttonPosition.second, buttonRadius, WHITE, 32); 376 | } 377 | 378 | else { 379 | drawList->AddCircle(buttonPosition.second, buttonRadius, WHITE, 32, 2 * scale); 380 | } 381 | 382 | if (buttonPosition.first == "XboxTypeS_A") { 383 | drawList->AddLine(ImVec2(buttonPosition.second.x - 5 * scale, buttonPosition.second.y - 5 * scale), ImVec2(buttonPosition.second.x + 5 * scale, buttonPosition.second.y + 5 * scale), inputs[buttonPosition.first].color, 2 * scale); 384 | drawList->AddLine(ImVec2(buttonPosition.second.x - 5 * scale, buttonPosition.second.y + 5 * scale), ImVec2(buttonPosition.second.x + 5 * scale, buttonPosition.second.y - 5 * scale), inputs[buttonPosition.first].color, 2 * scale); 385 | } 386 | 387 | else if (buttonPosition.first == "XboxTypeS_B") { 388 | drawList->AddCircle(buttonPosition.second, buttonRadius - 6 * scale, inputs[buttonPosition.first].color, 16, 2 * scale); 389 | } 390 | 391 | else if (buttonPosition.first == "XboxTypeS_X") { 392 | drawList->AddQuad(ImVec2(buttonPosition.second.x - 5 * scale, buttonPosition.second.y - 5 * scale), ImVec2(buttonPosition.second.x + 5 * scale, buttonPosition.second.y - 5 * scale), ImVec2(buttonPosition.second.x + 5 * scale, buttonPosition.second.y + 5 * scale), ImVec2(buttonPosition.second.x - 5 * scale, buttonPosition.second.y + 5 * scale), inputs[buttonPosition.first].color, 2 * scale); 393 | } 394 | 395 | else if (buttonPosition.first == "XboxTypeS_Y") { 396 | drawList->AddTriangle(ImVec2(buttonPosition.second.x, buttonPosition.second.y - 5 * scale), ImVec2(buttonPosition.second.x + 5 * scale, buttonPosition.second.y + 4 * scale), ImVec2(buttonPosition.second.x - 5 * scale, buttonPosition.second.y + 4 * scale), inputs[buttonPosition.first].color, 2 * scale); 397 | } 398 | } 399 | } 400 | 401 | ImGui::SetCursorPos(ImVec2((size == 0 ? cursorPosition.x + 73 : cursorPosition.x + 156), cursorPosition.y + 8 * scale)); 402 | 403 | if (ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) { 404 | if (ImGui::Button("Settings")) { 405 | renderSettings = !renderSettings; 406 | } 407 | } 408 | 409 | if (size == 1) { 410 | ImGui::PopFont(); 411 | } 412 | 413 | ImGui::PopStyleVar(); 414 | 415 | ImGui::End(); 416 | } 417 | 418 | std::string ControllerOverlay::GetMenuName() 419 | { 420 | return "controlleroverlay"; 421 | } 422 | 423 | std::string ControllerOverlay::GetMenuTitle() 424 | { 425 | return "Controller Overlay"; 426 | } 427 | 428 | void ControllerOverlay::SetImGuiContext(uintptr_t ctx) 429 | { 430 | ImGui::SetCurrentContext(reinterpret_cast(ctx)); 431 | } 432 | 433 | bool ControllerOverlay::ShouldBlockInput() 434 | { 435 | return false; 436 | } 437 | 438 | bool ControllerOverlay::IsActiveOverlay() 439 | { 440 | return false; 441 | } 442 | 443 | void ControllerOverlay::OnOpen() 444 | { 445 | renderControllerOverlay = true; 446 | } 447 | 448 | void ControllerOverlay::OnClose() 449 | { 450 | renderControllerOverlay = false; 451 | } 452 | -------------------------------------------------------------------------------- /ControllerOverlay/ControllerOverlay.h: -------------------------------------------------------------------------------- 1 | #pragma comment(lib, "pluginsdk.lib") 2 | 3 | #include 4 | 5 | #include "bakkesmod/plugin/bakkesmodplugin.h" 6 | #include "bakkesmod/plugin/pluginwindow.h" 7 | 8 | #include "imgui/imgui.h" 9 | 10 | #define RED ImColor(255, 0, 0, 255) 11 | #define BLUE ImColor(0, 0, 255, 255) 12 | #define GREEN ImColor(0, 255, 0, 255) 13 | #define DARKGREEN ImColor(0, 128, 0, 255) 14 | #define BLACK ImColor(0, 0, 0, 255) 15 | #define WHITE ImColor(255, 255, 255, 255) 16 | #define GREY ImColor(170, 170, 170, 255) 17 | #define DARKGREY ImColor(85, 85, 85, 255) 18 | #define YELLOW ImColor(255, 255, 0, 255) 19 | #define PURPLE ImColor(128, 0, 128, 255) 20 | 21 | struct Input { 22 | int index; 23 | bool pressed; 24 | ImColor color; 25 | std::string name; 26 | }; 27 | 28 | class ControllerOverlay : public BakkesMod::Plugin::BakkesModPlugin, public BakkesMod::Plugin::PluginWindow 29 | { 30 | public: 31 | void onLoad(); 32 | void onUnload(); 33 | 34 | void writeCfg(); 35 | 36 | void onTick(std::string eventName); 37 | 38 | void Render(); 39 | void RenderImGui(); 40 | std::string GetMenuName(); 41 | std::string GetMenuTitle(); 42 | void SetImGuiContext(uintptr_t ctx); 43 | bool ShouldBlockInput(); 44 | bool IsActiveOverlay(); 45 | void OnOpen(); 46 | void OnClose(); 47 | 48 | bool renderControllerOverlay = false; 49 | bool renderSettings = false; 50 | 51 | std::string configurationFilePath = "./bakkesmod/cfg/controlleroverlay.cfg"; 52 | 53 | bool titleBar = true; 54 | float transparency = 1.0f; 55 | int type = 0; 56 | int size = 0; 57 | 58 | std::map inputs; 59 | ControllerInput controllerInput; 60 | }; 61 | -------------------------------------------------------------------------------- /ControllerOverlay/ControllerOverlay.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | {48C21FDA-5BF0-47DD-B4F6-16303E299097} 24 | ControllerOverlay 25 | 10.0 26 | 27 | 28 | 29 | Application 30 | true 31 | v142 32 | MultiByte 33 | 34 | 35 | DynamicLibrary 36 | false 37 | v142 38 | true 39 | MultiByte 40 | 41 | 42 | Application 43 | true 44 | v142 45 | MultiByte 46 | 47 | 48 | DynamicLibrary 49 | false 50 | v142 51 | true 52 | MultiByte 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Level3 76 | Disabled 77 | true 78 | true 79 | 80 | 81 | Console 82 | 83 | 84 | 85 | 86 | Level3 87 | Disabled 88 | true 89 | true 90 | 91 | 92 | Console 93 | 94 | 95 | 96 | 97 | Level3 98 | MaxSpeed 99 | true 100 | true 101 | true 102 | true 103 | C:\Program Files %28x86%29\Steam\steamapps\common\rocketleague\Binaries\Win32\bakkesmod\bakkesmodsdk\include;%(AdditionalIncludeDirectories) 104 | _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 105 | 106 | 107 | Console 108 | true 109 | true 110 | C:\Program Files %28x86%29\Steam\steamapps\common\rocketleague\Binaries\Win32\bakkesmod\bakkesmodsdk\lib;%(AdditionalLibraryDirectories) 111 | 112 | 113 | copy /Y "$(TargetDir)$(ProjectName).dll" "C:\Program Files (x86)\Steam\steamapps\common\rocketleague\Binaries\Win32\bakkesmod\plugins" 114 | 115 | 116 | 117 | 118 | Level3 119 | MaxSpeed 120 | true 121 | true 122 | true 123 | true 124 | $(registry:HKEY_CURRENT_USER\Software\BakkesMod\AppPath@BakkesModPath)\bakkesmodsdk\include;%(AdditionalIncludeDirectories) 125 | _WINDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 126 | MultiThreaded 127 | 128 | 129 | Console 130 | true 131 | true 132 | $(registry:HKEY_CURRENT_USER\Software\BakkesMod\AppPath@BakkesModPath)\bakkesmodsdk\lib;%(AdditionalLibraryDirectories) 133 | 134 | 135 | copy /Y "$(TargetDir)$(ProjectName).dll" "$(registry:HKEY_CURRENT_USER\Software\BakkesMod\AppPath@BakkesModPath)\plugins" 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /ControllerOverlay/ControllerOverlay.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files 35 | 36 | 37 | Header Files 38 | 39 | 40 | Header Files 41 | 42 | 43 | Header Files 44 | 45 | 46 | Header Files 47 | 48 | 49 | Header Files 50 | 51 | 52 | Header Files 53 | 54 | 55 | Header Files 56 | 57 | 58 | Header Files 59 | 60 | 61 | 62 | 63 | Source Files 64 | 65 | 66 | Source Files 67 | 68 | 69 | Source Files 70 | 71 | 72 | Source Files 73 | 74 | 75 | Source Files 76 | 77 | 78 | Source Files 79 | 80 | 81 | Source Files 82 | 83 | 84 | Source Files 85 | 86 | 87 | Source Files 88 | 89 | 90 | Source Files 91 | 92 | 93 | Source Files 94 | 95 | 96 | Source Files 97 | 98 | 99 | -------------------------------------------------------------------------------- /ControllerOverlay/imgui/imconfig.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // COMPILE-TIME OPTIONS FOR DEAR IMGUI 3 | // Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure. 4 | // You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions. 5 | //----------------------------------------------------------------------------- 6 | // A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/branch with your modifications to imconfig.h) 7 | // B) or add configuration directives in your own file and compile with #define IMGUI_USER_CONFIG "myfilename.h" 8 | // If you do so you need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include 9 | // the imgui*.cpp files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures. 10 | // Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts. 11 | // Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using. 12 | //----------------------------------------------------------------------------- 13 | 14 | #pragma once 15 | 16 | //---- Define assertion handler. Defaults to calling assert(). 17 | // If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement. 18 | //#define IM_ASSERT(_EXPR) MyAssert(_EXPR) 19 | //#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts 20 | 21 | //---- Define attributes of all API symbols declarations, e.g. for DLL under Windows 22 | // Using dear imgui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility. 23 | //#define IMGUI_API __declspec( dllexport ) 24 | //#define IMGUI_API __declspec( dllimport ) 25 | 26 | //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names. 27 | //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS 28 | 29 | //---- Disable all of Dear ImGui or don't implement standard windows. 30 | // It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp. 31 | //#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty. 32 | //#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. Not recommended. 33 | //#define IMGUI_DISABLE_METRICS_WINDOW // Disable debug/metrics window: ShowMetricsWindow() will be empty. 34 | 35 | //---- Don't implement some functions to reduce linkage requirements. 36 | //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. 37 | //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow. 38 | //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime). 39 | //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). 40 | //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) 41 | //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. 42 | //#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. 43 | //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). 44 | 45 | //---- Include imgui_user.h at the end of imgui.h as a convenience 46 | //#define IMGUI_INCLUDE_IMGUI_USER_H 47 | 48 | //---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another) 49 | //#define IMGUI_USE_BGRA_PACKED_COLOR 50 | 51 | //---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version 52 | // By default the embedded implementations are declared static and not available outside of imgui cpp files. 53 | //#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" 54 | //#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" 55 | //#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION 56 | //#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION 57 | 58 | //---- Unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined, use the much faster STB sprintf library implementation of vsnprintf instead of the one from the default C library. 59 | // Note that stb_sprintf.h is meant to be provided by the user and available in the include path at compile time. Also, the compatibility checks of the arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by STB sprintf. 60 | // #define IMGUI_USE_STB_SPRINTF 61 | 62 | //---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4. 63 | // This will be inlined as part of ImVec2 and ImVec4 class declarations. 64 | /* 65 | #define IM_VEC2_CLASS_EXTRA \ 66 | ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \ 67 | operator MyVec2() const { return MyVec2(x,y); } 68 | 69 | #define IM_VEC4_CLASS_EXTRA \ 70 | ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \ 71 | operator MyVec4() const { return MyVec4(x,y,z,w); } 72 | */ 73 | 74 | //---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices. 75 | // Your renderer back-end will need to support it (most example renderer back-ends support both 16/32-bit indices). 76 | // Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer. 77 | // Read about ImGuiBackendFlags_RendererHasVtxOffset for details. 78 | //#define ImDrawIdx unsigned int 79 | 80 | //---- Override ImDrawCallback signature (will need to modify renderer back-ends accordingly) 81 | //struct ImDrawList; 82 | //struct ImDrawCmd; 83 | //typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data); 84 | //#define ImDrawCallback MyImDrawCallback 85 | 86 | //---- Debug Tools: Macro to break in Debugger 87 | // (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.) 88 | //#define IM_DEBUG_BREAK IM_ASSERT(0) 89 | //#define IM_DEBUG_BREAK __debugbreak() 90 | 91 | //---- Debug Tools: Have the Item Picker break in the ItemAdd() function instead of ItemHoverable(), 92 | // (which comes earlier in the code, will catch a few extra items, allow picking items other than Hovered one.) 93 | // This adds a small runtime cost which is why it is not enabled by default. 94 | //#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX 95 | 96 | //---- Debug Tools: Enable slower asserts 97 | //#define IMGUI_DEBUG_PARANOID 98 | 99 | //---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files. 100 | /* 101 | namespace ImGui 102 | { 103 | void MyFunction(const char* name, const MyMatrix44& v); 104 | } 105 | */ 106 | 107 | #define IMGUI_DEFINE_MATH_OPERATORS -------------------------------------------------------------------------------- /ControllerOverlay/imgui/imgui_additions.cpp: -------------------------------------------------------------------------------- 1 | #include "imgui_additions.h" 2 | 3 | namespace ImGui { 4 | bool Combo(const char* label, int* currIndex, std::vector& values) 5 | { 6 | if (values.empty()) { return false; } 7 | return Combo(label, currIndex, vector_getter, 8 | static_cast(&values), values.size()); 9 | } 10 | 11 | bool ListBox(const char* label, int* currIndex, std::vector& values) 12 | { 13 | if (values.empty()) { return false; } 14 | return ListBox(label, currIndex, vector_getter, 15 | static_cast(&values), values.size()); 16 | } 17 | } -------------------------------------------------------------------------------- /ControllerOverlay/imgui/imgui_additions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "imgui.h" 5 | #include "imgui_internal.h" 6 | 7 | namespace ImGui 8 | { 9 | static auto vector_getter = [](void* vec, int idx, const char** out_text) 10 | { 11 | auto& vector = *static_cast*>(vec); 12 | if (idx < 0 || idx >= static_cast(vector.size())) { return false; } 13 | *out_text = vector.at(idx).c_str(); 14 | return true; 15 | }; 16 | bool Combo(const char* label, int* currIndex, std::vector& values); 17 | bool ListBox(const char* label, int* currIndex, std::vector& values); 18 | } -------------------------------------------------------------------------------- /ControllerOverlay/imgui/imgui_impl_dx11.cpp: -------------------------------------------------------------------------------- 1 | // dear imgui: Renderer for DirectX11 2 | // This needs to be used along with a Platform Binding (e.g. Win32) 3 | 4 | // Implemented features: 5 | // [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! 6 | // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. 7 | 8 | // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. 9 | // If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp 10 | // https://github.com/ocornut/imgui 11 | 12 | // CHANGELOG 13 | // (minor and older changes stripped away, please see git history for details) 14 | // 2019-08-01: DirectX11: Fixed code querying the Geometry Shader state (would generally error with Debug layer enabled). 15 | // 2019-07-21: DirectX11: Backup, clear and restore Geometry Shader is any is bound when calling ImGui_ImplDX10_RenderDrawData. Clearing Hull/Domain/Compute shaders without backup/restore. 16 | // 2019-05-29: DirectX11: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. 17 | // 2019-04-30: DirectX11: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. 18 | // 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile(). 19 | // 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. 20 | // 2018-08-01: DirectX11: Querying for IDXGIFactory instead of IDXGIFactory1 to increase compatibility. 21 | // 2018-07-13: DirectX11: Fixed unreleased resources in Init and Shutdown functions. 22 | // 2018-06-08: Misc: Extracted imgui_impl_dx11.cpp/.h away from the old combined DX11+Win32 example. 23 | // 2018-06-08: DirectX11: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. 24 | // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX11_RenderDrawData() in the .h file so you can call it yourself. 25 | // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. 26 | // 2016-05-07: DirectX11: Disabling depth-write. 27 | 28 | #include "imgui.h" 29 | #include "imgui_impl_dx11.h" 30 | 31 | // DirectX 32 | #include 33 | #include 34 | #include 35 | #ifdef _MSC_VER 36 | #pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below. 37 | #endif 38 | 39 | // DirectX data 40 | static ID3D11Device* g_pd3dDevice = NULL; 41 | static ID3D11DeviceContext* g_pd3dDeviceContext = NULL; 42 | static IDXGIFactory* g_pFactory = NULL; 43 | static ID3D11Buffer* g_pVB = NULL; 44 | static ID3D11Buffer* g_pIB = NULL; 45 | static ID3D10Blob* g_pVertexShaderBlob = NULL; 46 | static ID3D11VertexShader* g_pVertexShader = NULL; 47 | static ID3D11InputLayout* g_pInputLayout = NULL; 48 | static ID3D11Buffer* g_pVertexConstantBuffer = NULL; 49 | static ID3D10Blob* g_pPixelShaderBlob = NULL; 50 | static ID3D11PixelShader* g_pPixelShader = NULL; 51 | static ID3D11SamplerState* g_pFontSampler = NULL; 52 | static ID3D11ShaderResourceView* g_pFontTextureView = NULL; 53 | static ID3D11RasterizerState* g_pRasterizerState = NULL; 54 | static ID3D11BlendState* g_pBlendState = NULL; 55 | static ID3D11DepthStencilState* g_pDepthStencilState = NULL; 56 | static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000; 57 | 58 | struct VERTEX_CONSTANT_BUFFER 59 | { 60 | float mvp[4][4]; 61 | }; 62 | 63 | static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceContext* ctx) 64 | { 65 | // Setup viewport 66 | D3D11_VIEWPORT vp; 67 | memset(&vp, 0, sizeof(D3D11_VIEWPORT)); 68 | vp.Width = draw_data->DisplaySize.x; 69 | vp.Height = draw_data->DisplaySize.y; 70 | vp.MinDepth = 0.0f; 71 | vp.MaxDepth = 1.0f; 72 | vp.TopLeftX = vp.TopLeftY = 0; 73 | ctx->RSSetViewports(1, &vp); 74 | 75 | // Setup shader and vertex buffers 76 | unsigned int stride = sizeof(ImDrawVert); 77 | unsigned int offset = 0; 78 | ctx->IASetInputLayout(g_pInputLayout); 79 | ctx->IASetVertexBuffers(0, 1, &g_pVB, &stride, &offset); 80 | ctx->IASetIndexBuffer(g_pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0); 81 | ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); 82 | ctx->VSSetShader(g_pVertexShader, NULL, 0); 83 | ctx->VSSetConstantBuffers(0, 1, &g_pVertexConstantBuffer); 84 | ctx->PSSetShader(g_pPixelShader, NULL, 0); 85 | ctx->PSSetSamplers(0, 1, &g_pFontSampler); 86 | ctx->GSSetShader(NULL, NULL, 0); 87 | ctx->HSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used.. 88 | ctx->DSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used.. 89 | ctx->CSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used.. 90 | 91 | // Setup blend state 92 | const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f }; 93 | ctx->OMSetBlendState(g_pBlendState, blend_factor, 0xffffffff); 94 | ctx->OMSetDepthStencilState(g_pDepthStencilState, 0); 95 | ctx->RSSetState(g_pRasterizerState); 96 | } 97 | 98 | // Render function 99 | // (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) 100 | void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) 101 | { 102 | // Avoid rendering when minimized 103 | if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) 104 | return; 105 | 106 | ID3D11DeviceContext* ctx = g_pd3dDeviceContext; 107 | 108 | // Create and grow vertex/index buffers if needed 109 | if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount) 110 | { 111 | if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } 112 | g_VertexBufferSize = draw_data->TotalVtxCount + 5000; 113 | D3D11_BUFFER_DESC desc; 114 | memset(&desc, 0, sizeof(D3D11_BUFFER_DESC)); 115 | desc.Usage = D3D11_USAGE_DYNAMIC; 116 | desc.ByteWidth = g_VertexBufferSize * sizeof(ImDrawVert); 117 | desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; 118 | desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; 119 | desc.MiscFlags = 0; 120 | if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVB) < 0) 121 | return; 122 | } 123 | if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount) 124 | { 125 | if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } 126 | g_IndexBufferSize = draw_data->TotalIdxCount + 10000; 127 | D3D11_BUFFER_DESC desc; 128 | memset(&desc, 0, sizeof(D3D11_BUFFER_DESC)); 129 | desc.Usage = D3D11_USAGE_DYNAMIC; 130 | desc.ByteWidth = g_IndexBufferSize * sizeof(ImDrawIdx); 131 | desc.BindFlags = D3D11_BIND_INDEX_BUFFER; 132 | desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; 133 | if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pIB) < 0) 134 | return; 135 | } 136 | 137 | // Upload vertex/index data into a single contiguous GPU buffer 138 | D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource; 139 | if (ctx->Map(g_pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK) 140 | return; 141 | if (ctx->Map(g_pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK) 142 | return; 143 | ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData; 144 | ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData; 145 | for (int n = 0; n < draw_data->CmdListsCount; n++) 146 | { 147 | const ImDrawList* cmd_list = draw_data->CmdLists[n]; 148 | memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert)); 149 | memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx)); 150 | vtx_dst += cmd_list->VtxBuffer.Size; 151 | idx_dst += cmd_list->IdxBuffer.Size; 152 | } 153 | ctx->Unmap(g_pVB, 0); 154 | ctx->Unmap(g_pIB, 0); 155 | 156 | // Setup orthographic projection matrix into our constant buffer 157 | // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. 158 | { 159 | D3D11_MAPPED_SUBRESOURCE mapped_resource; 160 | if (ctx->Map(g_pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) 161 | return; 162 | VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource.pData; 163 | float L = draw_data->DisplayPos.x; 164 | float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; 165 | float T = draw_data->DisplayPos.y; 166 | float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; 167 | float mvp[4][4] = 168 | { 169 | { 2.0f / (R - L), 0.0f, 0.0f, 0.0f }, 170 | { 0.0f, 2.0f / (T - B), 0.0f, 0.0f }, 171 | { 0.0f, 0.0f, 0.5f, 0.0f }, 172 | { (R + L) / (L - R), (T + B) / (B - T), 0.5f, 1.0f }, 173 | }; 174 | memcpy(&constant_buffer->mvp, mvp, sizeof(mvp)); 175 | ctx->Unmap(g_pVertexConstantBuffer, 0); 176 | } 177 | 178 | // Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!) 179 | struct BACKUP_DX11_STATE 180 | { 181 | UINT ScissorRectsCount, ViewportsCount; 182 | D3D11_RECT ScissorRects[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE]; 183 | D3D11_VIEWPORT Viewports[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE]; 184 | ID3D11RasterizerState* RS; 185 | ID3D11BlendState* BlendState; 186 | FLOAT BlendFactor[4]; 187 | UINT SampleMask; 188 | UINT StencilRef; 189 | ID3D11DepthStencilState* DepthStencilState; 190 | ID3D11ShaderResourceView* PSShaderResource; 191 | ID3D11SamplerState* PSSampler; 192 | ID3D11PixelShader* PS; 193 | ID3D11VertexShader* VS; 194 | ID3D11GeometryShader* GS; 195 | UINT PSInstancesCount, VSInstancesCount, GSInstancesCount; 196 | ID3D11ClassInstance* PSInstances[256], * VSInstances[256], * GSInstances[256]; // 256 is max according to PSSetShader documentation 197 | D3D11_PRIMITIVE_TOPOLOGY PrimitiveTopology; 198 | ID3D11Buffer* IndexBuffer, * VertexBuffer, * VSConstantBuffer; 199 | UINT IndexBufferOffset, VertexBufferStride, VertexBufferOffset; 200 | DXGI_FORMAT IndexBufferFormat; 201 | ID3D11InputLayout* InputLayout; 202 | }; 203 | BACKUP_DX11_STATE old; 204 | old.ScissorRectsCount = old.ViewportsCount = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; 205 | ctx->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects); 206 | ctx->RSGetViewports(&old.ViewportsCount, old.Viewports); 207 | ctx->RSGetState(&old.RS); 208 | ctx->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask); 209 | ctx->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef); 210 | ctx->PSGetShaderResources(0, 1, &old.PSShaderResource); 211 | ctx->PSGetSamplers(0, 1, &old.PSSampler); 212 | old.PSInstancesCount = old.VSInstancesCount = old.GSInstancesCount = 256; 213 | ctx->PSGetShader(&old.PS, old.PSInstances, &old.PSInstancesCount); 214 | ctx->VSGetShader(&old.VS, old.VSInstances, &old.VSInstancesCount); 215 | ctx->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer); 216 | ctx->GSGetShader(&old.GS, old.GSInstances, &old.GSInstancesCount); 217 | 218 | ctx->IAGetPrimitiveTopology(&old.PrimitiveTopology); 219 | ctx->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset); 220 | ctx->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); 221 | ctx->IAGetInputLayout(&old.InputLayout); 222 | 223 | // Setup desired DX state 224 | ImGui_ImplDX11_SetupRenderState(draw_data, ctx); 225 | 226 | // Render command lists 227 | // (Because we merged all buffers into a single one, we maintain our own offset into them) 228 | int global_idx_offset = 0; 229 | int global_vtx_offset = 0; 230 | ImVec2 clip_off = draw_data->DisplayPos; 231 | for (int n = 0; n < draw_data->CmdListsCount; n++) 232 | { 233 | const ImDrawList* cmd_list = draw_data->CmdLists[n]; 234 | for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) 235 | { 236 | const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; 237 | if (pcmd->UserCallback != NULL) 238 | { 239 | // User callback, registered via ImDrawList::AddCallback() 240 | // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) 241 | if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) 242 | ImGui_ImplDX11_SetupRenderState(draw_data, ctx); 243 | else 244 | pcmd->UserCallback(cmd_list, pcmd); 245 | } 246 | else 247 | { 248 | // Apply scissor/clipping rectangle 249 | const D3D11_RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y) }; 250 | ctx->RSSetScissorRects(1, &r); 251 | 252 | // Bind texture, Draw 253 | ID3D11ShaderResourceView* texture_srv = (ID3D11ShaderResourceView*)pcmd->TextureId; 254 | ctx->PSSetShaderResources(0, 1, &texture_srv); 255 | ctx->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset); 256 | } 257 | } 258 | global_idx_offset += cmd_list->IdxBuffer.Size; 259 | global_vtx_offset += cmd_list->VtxBuffer.Size; 260 | } 261 | 262 | // Restore modified DX state 263 | ctx->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects); 264 | ctx->RSSetViewports(old.ViewportsCount, old.Viewports); 265 | ctx->RSSetState(old.RS); if (old.RS) old.RS->Release(); 266 | ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release(); 267 | ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release(); 268 | ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release(); 269 | ctx->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release(); 270 | ctx->PSSetShader(old.PS, old.PSInstances, old.PSInstancesCount); if (old.PS) old.PS->Release(); 271 | for (UINT i = 0; i < old.PSInstancesCount; i++) if (old.PSInstances[i]) old.PSInstances[i]->Release(); 272 | ctx->VSSetShader(old.VS, old.VSInstances, old.VSInstancesCount); if (old.VS) old.VS->Release(); 273 | ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release(); 274 | ctx->GSSetShader(old.GS, old.GSInstances, old.GSInstancesCount); if (old.GS) old.GS->Release(); 275 | for (UINT i = 0; i < old.VSInstancesCount; i++) if (old.VSInstances[i]) old.VSInstances[i]->Release(); 276 | ctx->IASetPrimitiveTopology(old.PrimitiveTopology); 277 | ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release(); 278 | ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release(); 279 | ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release(); 280 | } 281 | 282 | static void ImGui_ImplDX11_CreateFontsTexture() 283 | { 284 | // Build texture atlas 285 | ImGuiIO& io = ImGui::GetIO(); 286 | unsigned char* pixels; 287 | int width, height; 288 | io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); 289 | 290 | // Upload texture to graphics system 291 | { 292 | D3D11_TEXTURE2D_DESC desc; 293 | ZeroMemory(&desc, sizeof(desc)); 294 | desc.Width = width; 295 | desc.Height = height; 296 | desc.MipLevels = 1; 297 | desc.ArraySize = 1; 298 | desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 299 | desc.SampleDesc.Count = 1; 300 | desc.Usage = D3D11_USAGE_DEFAULT; 301 | desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; 302 | desc.CPUAccessFlags = 0; 303 | 304 | ID3D11Texture2D* pTexture = NULL; 305 | D3D11_SUBRESOURCE_DATA subResource; 306 | subResource.pSysMem = pixels; 307 | subResource.SysMemPitch = desc.Width * 4; 308 | subResource.SysMemSlicePitch = 0; 309 | g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture); 310 | 311 | // Create texture view 312 | D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; 313 | ZeroMemory(&srvDesc, sizeof(srvDesc)); 314 | srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 315 | srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; 316 | srvDesc.Texture2D.MipLevels = desc.MipLevels; 317 | srvDesc.Texture2D.MostDetailedMip = 0; 318 | g_pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &g_pFontTextureView); 319 | pTexture->Release(); 320 | } 321 | 322 | // Store our identifier 323 | io.Fonts->TexID = (ImTextureID)g_pFontTextureView; 324 | 325 | // Create texture sampler 326 | { 327 | D3D11_SAMPLER_DESC desc; 328 | ZeroMemory(&desc, sizeof(desc)); 329 | desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; 330 | desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; 331 | desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; 332 | desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; 333 | desc.MipLODBias = 0.f; 334 | desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; 335 | desc.MinLOD = 0.f; 336 | desc.MaxLOD = 0.f; 337 | g_pd3dDevice->CreateSamplerState(&desc, &g_pFontSampler); 338 | } 339 | } 340 | 341 | bool ImGui_ImplDX11_CreateDeviceObjects() 342 | { 343 | if (!g_pd3dDevice) 344 | return false; 345 | if (g_pFontSampler) 346 | ImGui_ImplDX11_InvalidateDeviceObjects(); 347 | 348 | // By using D3DCompile() from / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A) 349 | // If you would like to use this DX11 sample code but remove this dependency you can: 350 | // 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution] 351 | // 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL. 352 | // See https://github.com/ocornut/imgui/pull/638 for sources and details. 353 | 354 | // Create the vertex shader 355 | { 356 | static const char* vertexShader = 357 | "cbuffer vertexBuffer : register(b0) \ 358 | {\ 359 | float4x4 ProjectionMatrix; \ 360 | };\ 361 | struct VS_INPUT\ 362 | {\ 363 | float2 pos : POSITION;\ 364 | float4 col : COLOR0;\ 365 | float2 uv : TEXCOORD0;\ 366 | };\ 367 | \ 368 | struct PS_INPUT\ 369 | {\ 370 | float4 pos : SV_POSITION;\ 371 | float4 col : COLOR0;\ 372 | float2 uv : TEXCOORD0;\ 373 | };\ 374 | \ 375 | PS_INPUT main(VS_INPUT input)\ 376 | {\ 377 | PS_INPUT output;\ 378 | output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\ 379 | output.col = input.col;\ 380 | output.uv = input.uv;\ 381 | return output;\ 382 | }"; 383 | 384 | D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &g_pVertexShaderBlob, NULL); 385 | if (g_pVertexShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! 386 | return false; 387 | if (g_pd3dDevice->CreateVertexShader((DWORD*)g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize(), NULL, &g_pVertexShader) != S_OK) 388 | return false; 389 | 390 | // Create the input layout 391 | D3D11_INPUT_ELEMENT_DESC local_layout[] = 392 | { 393 | { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (size_t)(&((ImDrawVert*)0)->pos), D3D11_INPUT_PER_VERTEX_DATA, 0 }, 394 | { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (size_t)(&((ImDrawVert*)0)->uv), D3D11_INPUT_PER_VERTEX_DATA, 0 }, 395 | { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (size_t)(&((ImDrawVert*)0)->col), D3D11_INPUT_PER_VERTEX_DATA, 0 }, 396 | }; 397 | if (g_pd3dDevice->CreateInputLayout(local_layout, 3, g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize(), &g_pInputLayout) != S_OK) 398 | return false; 399 | 400 | // Create the constant buffer 401 | { 402 | D3D11_BUFFER_DESC desc; 403 | desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER); 404 | desc.Usage = D3D11_USAGE_DYNAMIC; 405 | desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; 406 | desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; 407 | desc.MiscFlags = 0; 408 | g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVertexConstantBuffer); 409 | } 410 | } 411 | 412 | // Create the pixel shader 413 | { 414 | static const char* pixelShader = 415 | "struct PS_INPUT\ 416 | {\ 417 | float4 pos : SV_POSITION;\ 418 | float4 col : COLOR0;\ 419 | float2 uv : TEXCOORD0;\ 420 | };\ 421 | sampler sampler0;\ 422 | Texture2D texture0;\ 423 | \ 424 | float4 main(PS_INPUT input) : SV_Target\ 425 | {\ 426 | float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \ 427 | return out_col; \ 428 | }"; 429 | 430 | D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &g_pPixelShaderBlob, NULL); 431 | if (g_pPixelShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! 432 | return false; 433 | if (g_pd3dDevice->CreatePixelShader((DWORD*)g_pPixelShaderBlob->GetBufferPointer(), g_pPixelShaderBlob->GetBufferSize(), NULL, &g_pPixelShader) != S_OK) 434 | return false; 435 | } 436 | 437 | // Create the blending setup 438 | { 439 | D3D11_BLEND_DESC desc; 440 | ZeroMemory(&desc, sizeof(desc)); 441 | desc.AlphaToCoverageEnable = false; 442 | desc.RenderTarget[0].BlendEnable = true; 443 | desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; 444 | desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; 445 | desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; 446 | desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; 447 | desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; 448 | desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; 449 | desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; 450 | g_pd3dDevice->CreateBlendState(&desc, &g_pBlendState); 451 | } 452 | 453 | // Create the rasterizer state 454 | { 455 | D3D11_RASTERIZER_DESC desc; 456 | ZeroMemory(&desc, sizeof(desc)); 457 | desc.FillMode = D3D11_FILL_SOLID; 458 | desc.CullMode = D3D11_CULL_NONE; 459 | desc.ScissorEnable = true; 460 | desc.DepthClipEnable = true; 461 | g_pd3dDevice->CreateRasterizerState(&desc, &g_pRasterizerState); 462 | } 463 | 464 | // Create depth-stencil State 465 | { 466 | D3D11_DEPTH_STENCIL_DESC desc; 467 | ZeroMemory(&desc, sizeof(desc)); 468 | desc.DepthEnable = false; 469 | desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; 470 | desc.DepthFunc = D3D11_COMPARISON_ALWAYS; 471 | desc.StencilEnable = false; 472 | desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; 473 | desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; 474 | desc.BackFace = desc.FrontFace; 475 | g_pd3dDevice->CreateDepthStencilState(&desc, &g_pDepthStencilState); 476 | } 477 | 478 | ImGui_ImplDX11_CreateFontsTexture(); 479 | 480 | return true; 481 | } 482 | 483 | void ImGui_ImplDX11_InvalidateDeviceObjects() 484 | { 485 | if (!g_pd3dDevice) 486 | return; 487 | 488 | if (g_pFontSampler) { g_pFontSampler->Release(); g_pFontSampler = NULL; } 489 | if (g_pFontTextureView) { g_pFontTextureView->Release(); g_pFontTextureView = NULL; ImGui::GetIO().Fonts->TexID = NULL; } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. 490 | if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } 491 | if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } 492 | 493 | if (g_pBlendState) { g_pBlendState->Release(); g_pBlendState = NULL; } 494 | if (g_pDepthStencilState) { g_pDepthStencilState->Release(); g_pDepthStencilState = NULL; } 495 | if (g_pRasterizerState) { g_pRasterizerState->Release(); g_pRasterizerState = NULL; } 496 | if (g_pPixelShader) { g_pPixelShader->Release(); g_pPixelShader = NULL; } 497 | if (g_pPixelShaderBlob) { g_pPixelShaderBlob->Release(); g_pPixelShaderBlob = NULL; } 498 | if (g_pVertexConstantBuffer) { g_pVertexConstantBuffer->Release(); g_pVertexConstantBuffer = NULL; } 499 | if (g_pInputLayout) { g_pInputLayout->Release(); g_pInputLayout = NULL; } 500 | if (g_pVertexShader) { g_pVertexShader->Release(); g_pVertexShader = NULL; } 501 | if (g_pVertexShaderBlob) { g_pVertexShaderBlob->Release(); g_pVertexShaderBlob = NULL; } 502 | } 503 | 504 | bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context) 505 | { 506 | // Setup back-end capabilities flags 507 | ImGuiIO& io = ImGui::GetIO(); 508 | io.BackendRendererName = "imgui_impl_dx11"; 509 | io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. 510 | 511 | // Get factory from device 512 | IDXGIDevice* pDXGIDevice = NULL; 513 | IDXGIAdapter* pDXGIAdapter = NULL; 514 | IDXGIFactory* pFactory = NULL; 515 | 516 | if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) == S_OK) 517 | if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK) 518 | if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK) 519 | { 520 | g_pd3dDevice = device; 521 | g_pd3dDeviceContext = device_context; 522 | g_pFactory = pFactory; 523 | } 524 | if (pDXGIDevice) pDXGIDevice->Release(); 525 | if (pDXGIAdapter) pDXGIAdapter->Release(); 526 | g_pd3dDevice->AddRef(); 527 | g_pd3dDeviceContext->AddRef(); 528 | 529 | return true; 530 | } 531 | 532 | void ImGui_ImplDX11_Shutdown() 533 | { 534 | ImGui_ImplDX11_InvalidateDeviceObjects(); 535 | if (g_pFactory) { g_pFactory->Release(); g_pFactory = NULL; } 536 | if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; } 537 | if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = NULL; } 538 | } 539 | 540 | void ImGui_ImplDX11_NewFrame() 541 | { 542 | if (!g_pFontSampler) 543 | ImGui_ImplDX11_CreateDeviceObjects(); 544 | } -------------------------------------------------------------------------------- /ControllerOverlay/imgui/imgui_impl_dx11.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Renderer for DirectX11 2 | // This needs to be used along with a Platform Binding (e.g. Win32) 3 | 4 | // Implemented features: 5 | // [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! 6 | // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. 7 | 8 | // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. 9 | // If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. 10 | // https://github.com/ocornut/imgui 11 | 12 | #pragma once 13 | 14 | struct ID3D11Device; 15 | struct ID3D11DeviceContext; 16 | 17 | IMGUI_IMPL_API bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context); 18 | IMGUI_IMPL_API void ImGui_ImplDX11_Shutdown(); 19 | IMGUI_IMPL_API void ImGui_ImplDX11_NewFrame(); 20 | IMGUI_IMPL_API void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data); 21 | 22 | // Use if you want to reset your rendering device without losing ImGui state. 23 | IMGUI_IMPL_API void ImGui_ImplDX11_InvalidateDeviceObjects(); 24 | IMGUI_IMPL_API bool ImGui_ImplDX11_CreateDeviceObjects(); -------------------------------------------------------------------------------- /ControllerOverlay/imgui/imgui_impl_win32.cpp: -------------------------------------------------------------------------------- 1 | // dear imgui: Platform Binding for Windows (standard windows API for 32 and 64 bits applications) 2 | // This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) 3 | 4 | // Implemented features: 5 | // [X] Platform: Clipboard support (for Win32 this is actually part of core imgui) 6 | // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. 7 | // [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE). 8 | // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. 9 | 10 | #include "imgui.h" 11 | #include "imgui_impl_win32.h" 12 | #ifndef WIN32_LEAN_AND_MEAN 13 | #define WIN32_LEAN_AND_MEAN 14 | #endif 15 | #include 16 | #include 17 | 18 | // Using XInput library for gamepad (with recent Windows SDK this may leads to executables which won't run on Windows 7) 19 | #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD 20 | #include 21 | #else 22 | #define IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT 23 | #endif 24 | #if defined(_MSC_VER) && !defined(IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT) 25 | #pragma comment(lib, "xinput") 26 | //#pragma comment(lib, "Xinput9_1_0") 27 | #endif 28 | 29 | // CHANGELOG 30 | // (minor and older changes stripped away, please see git history for details) 31 | // 2020-01-14: Inputs: Added support for #define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD/IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT. 32 | // 2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor. 33 | // 2019-05-11: Inputs: Don't filter value from WM_CHAR before calling AddInputCharacter(). 34 | // 2019-01-17: Misc: Using GetForegroundWindow()+IsChild() instead of GetActiveWindow() to be compatible with windows created in a different thread or parent. 35 | // 2019-01-17: Inputs: Added support for mouse buttons 4 and 5 via WM_XBUTTON* messages. 36 | // 2019-01-15: Inputs: Added support for XInput gamepads (if ImGuiConfigFlags_NavEnableGamepad is set by user application). 37 | // 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. 38 | // 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor. 39 | // 2018-06-10: Inputs: Fixed handling of mouse wheel messages to support fine position messages (typically sent by track-pads). 40 | // 2018-06-08: Misc: Extracted imgui_impl_win32.cpp/.h away from the old combined DX9/DX10/DX11/DX12 examples. 41 | // 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors and ImGuiBackendFlags_HasSetMousePos flags + honor ImGuiConfigFlags_NoMouseCursorChange flag. 42 | // 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling). 43 | // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. 44 | // 2018-02-06: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). 45 | // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. 46 | // 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. 47 | // 2018-01-08: Inputs: Added mapping for ImGuiKey_Insert. 48 | // 2018-01-05: Inputs: Added WM_LBUTTONDBLCLK double-click handlers for window classes with the CS_DBLCLKS flag. 49 | // 2017-10-23: Inputs: Added WM_SYSKEYDOWN / WM_SYSKEYUP handlers so e.g. the VK_MENU key can be read. 50 | // 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging. 51 | // 2016-11-12: Inputs: Only call Win32 ::SetCursor(NULL) when io.MouseDrawCursor is set. 52 | 53 | // Win32 Data 54 | static HWND g_hWnd = NULL; 55 | static INT64 g_Time = 0; 56 | static INT64 g_TicksPerSecond = 0; 57 | static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT; 58 | static bool g_HasGamepad = false; 59 | static bool g_WantUpdateHasGamepad = true; 60 | 61 | // Functions 62 | bool ImGui_ImplWin32_Init(void* hwnd) 63 | { 64 | if (!::QueryPerformanceFrequency((LARGE_INTEGER*)&g_TicksPerSecond)) 65 | return false; 66 | if (!::QueryPerformanceCounter((LARGE_INTEGER*)&g_Time)) 67 | return false; 68 | 69 | // Setup back-end capabilities flags 70 | g_hWnd = (HWND)hwnd; 71 | ImGuiIO& io = ImGui::GetIO(); 72 | io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) 73 | io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) 74 | io.BackendPlatformName = "imgui_impl_win32"; 75 | io.ImeWindowHandle = hwnd; 76 | 77 | // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime. 78 | io.KeyMap[ImGuiKey_Tab] = VK_TAB; 79 | io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT; 80 | io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT; 81 | io.KeyMap[ImGuiKey_UpArrow] = VK_UP; 82 | io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN; 83 | io.KeyMap[ImGuiKey_PageUp] = VK_PRIOR; 84 | io.KeyMap[ImGuiKey_PageDown] = VK_NEXT; 85 | io.KeyMap[ImGuiKey_Home] = VK_HOME; 86 | io.KeyMap[ImGuiKey_End] = VK_END; 87 | io.KeyMap[ImGuiKey_Insert] = VK_INSERT; 88 | io.KeyMap[ImGuiKey_Delete] = VK_DELETE; 89 | io.KeyMap[ImGuiKey_Backspace] = VK_BACK; 90 | io.KeyMap[ImGuiKey_Space] = VK_SPACE; 91 | io.KeyMap[ImGuiKey_Enter] = VK_RETURN; 92 | io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE; 93 | io.KeyMap[ImGuiKey_KeyPadEnter] = VK_RETURN; 94 | io.KeyMap[ImGuiKey_A] = 'A'; 95 | io.KeyMap[ImGuiKey_C] = 'C'; 96 | io.KeyMap[ImGuiKey_V] = 'V'; 97 | io.KeyMap[ImGuiKey_X] = 'X'; 98 | io.KeyMap[ImGuiKey_Y] = 'Y'; 99 | io.KeyMap[ImGuiKey_Z] = 'Z'; 100 | 101 | return true; 102 | } 103 | 104 | void ImGui_ImplWin32_Shutdown() 105 | { 106 | g_hWnd = (HWND)0; 107 | } 108 | 109 | static bool ImGui_ImplWin32_UpdateMouseCursor() 110 | { 111 | ImGuiIO& io = ImGui::GetIO(); 112 | if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) 113 | return false; 114 | 115 | ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); 116 | if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) 117 | { 118 | // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor 119 | ::SetCursor(NULL); 120 | } 121 | else 122 | { 123 | // Show OS mouse cursor 124 | LPTSTR win32_cursor = IDC_ARROW; 125 | switch (imgui_cursor) 126 | { 127 | case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break; 128 | case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break; 129 | case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break; 130 | case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break; 131 | case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break; 132 | case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break; 133 | case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break; 134 | case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break; 135 | case ImGuiMouseCursor_NotAllowed: win32_cursor = IDC_NO; break; 136 | } 137 | ::SetCursor(::LoadCursor(NULL, win32_cursor)); 138 | } 139 | return true; 140 | } 141 | 142 | static void ImGui_ImplWin32_UpdateMousePos() 143 | { 144 | ImGuiIO& io = ImGui::GetIO(); 145 | 146 | // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) 147 | if (io.WantSetMousePos) 148 | { 149 | POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; 150 | ::ClientToScreen(g_hWnd, &pos); 151 | ::SetCursorPos(pos.x, pos.y); 152 | } 153 | 154 | // Set mouse position 155 | io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); 156 | POINT pos; 157 | if (HWND active_window = ::GetForegroundWindow()) 158 | if (active_window == g_hWnd || ::IsChild(active_window, g_hWnd)) 159 | if (::GetCursorPos(&pos) && ::ScreenToClient(g_hWnd, &pos)) 160 | io.MousePos = ImVec2((float)pos.x, (float)pos.y); 161 | } 162 | 163 | // Gamepad navigation mapping 164 | static void ImGui_ImplWin32_UpdateGamepads() 165 | { 166 | #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD 167 | ImGuiIO& io = ImGui::GetIO(); 168 | memset(io.NavInputs, 0, sizeof(io.NavInputs)); 169 | if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) 170 | return; 171 | 172 | // Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow. 173 | // Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE. 174 | if (g_WantUpdateHasGamepad) 175 | { 176 | XINPUT_CAPABILITIES caps; 177 | g_HasGamepad = (XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS); 178 | g_WantUpdateHasGamepad = false; 179 | } 180 | 181 | XINPUT_STATE xinput_state; 182 | io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; 183 | if (g_HasGamepad && XInputGetState(0, &xinput_state) == ERROR_SUCCESS) 184 | { 185 | const XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad; 186 | io.BackendFlags |= ImGuiBackendFlags_HasGamepad; 187 | 188 | #define MAP_BUTTON(NAV_NO, BUTTON_ENUM) { io.NavInputs[NAV_NO] = (gamepad.wButtons & BUTTON_ENUM) ? 1.0f : 0.0f; } 189 | #define MAP_ANALOG(NAV_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); if (vn > 1.0f) vn = 1.0f; if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; } 190 | MAP_BUTTON(ImGuiNavInput_Activate, XINPUT_GAMEPAD_A); // Cross / A 191 | MAP_BUTTON(ImGuiNavInput_Cancel, XINPUT_GAMEPAD_B); // Circle / B 192 | MAP_BUTTON(ImGuiNavInput_Menu, XINPUT_GAMEPAD_X); // Square / X 193 | MAP_BUTTON(ImGuiNavInput_Input, XINPUT_GAMEPAD_Y); // Triangle / Y 194 | MAP_BUTTON(ImGuiNavInput_DpadLeft, XINPUT_GAMEPAD_DPAD_LEFT); // D-Pad Left 195 | MAP_BUTTON(ImGuiNavInput_DpadRight, XINPUT_GAMEPAD_DPAD_RIGHT); // D-Pad Right 196 | MAP_BUTTON(ImGuiNavInput_DpadUp, XINPUT_GAMEPAD_DPAD_UP); // D-Pad Up 197 | MAP_BUTTON(ImGuiNavInput_DpadDown, XINPUT_GAMEPAD_DPAD_DOWN); // D-Pad Down 198 | MAP_BUTTON(ImGuiNavInput_FocusPrev, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB 199 | MAP_BUTTON(ImGuiNavInput_FocusNext, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB 200 | MAP_BUTTON(ImGuiNavInput_TweakSlow, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB 201 | MAP_BUTTON(ImGuiNavInput_TweakFast, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB 202 | MAP_ANALOG(ImGuiNavInput_LStickLeft, gamepad.sThumbLX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768); 203 | MAP_ANALOG(ImGuiNavInput_LStickRight, gamepad.sThumbLX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); 204 | MAP_ANALOG(ImGuiNavInput_LStickUp, gamepad.sThumbLY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); 205 | MAP_ANALOG(ImGuiNavInput_LStickDown, gamepad.sThumbLY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32767); 206 | #undef MAP_BUTTON 207 | #undef MAP_ANALOG 208 | } 209 | #endif // #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD 210 | } 211 | 212 | void ImGui_ImplWin32_NewFrame() 213 | { 214 | ImGuiIO& io = ImGui::GetIO(); 215 | IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame()."); 216 | 217 | // Setup display size (every frame to accommodate for window resizing) 218 | RECT rect; 219 | ::GetClientRect(g_hWnd, &rect); 220 | io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top)); 221 | 222 | // Setup time step 223 | INT64 current_time; 224 | ::QueryPerformanceCounter((LARGE_INTEGER*)¤t_time); 225 | io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond; 226 | g_Time = current_time; 227 | 228 | // Read keyboard modifiers inputs 229 | io.KeyCtrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0; 230 | io.KeyShift = (::GetKeyState(VK_SHIFT) & 0x8000) != 0; 231 | io.KeyAlt = (::GetKeyState(VK_MENU) & 0x8000) != 0; 232 | io.KeySuper = false; 233 | // io.KeysDown[], io.MousePos, io.MouseDown[], io.MouseWheel: filled by the WndProc handler below. 234 | 235 | // Update OS mouse position 236 | ImGui_ImplWin32_UpdateMousePos(); 237 | 238 | // Update OS mouse cursor with the cursor requested by imgui 239 | ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor(); 240 | if (g_LastMouseCursor != mouse_cursor) 241 | { 242 | g_LastMouseCursor = mouse_cursor; 243 | ImGui_ImplWin32_UpdateMouseCursor(); 244 | } 245 | 246 | // Update game controllers (if enabled and available) 247 | ImGui_ImplWin32_UpdateGamepads(); 248 | } 249 | 250 | // Allow compilation with old Windows SDK. MinGW doesn't have default _WIN32_WINNT/WINVER versions. 251 | #ifndef WM_MOUSEHWHEEL 252 | #define WM_MOUSEHWHEEL 0x020E 253 | #endif 254 | #ifndef DBT_DEVNODES_CHANGED 255 | #define DBT_DEVNODES_CHANGED 0x0007 256 | #endif 257 | 258 | // Process Win32 mouse/keyboard inputs. 259 | // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. 260 | // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. 261 | // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. 262 | // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. 263 | // PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinates when dragging mouse outside of our window bounds. 264 | // PS: We treat DBLCLK messages as regular mouse down messages, so this code will work on windows classes that have the CS_DBLCLKS flag set. Our own example app code doesn't set this flag. 265 | IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 266 | { 267 | if (ImGui::GetCurrentContext() == NULL) 268 | return 0; 269 | 270 | ImGuiIO& io = ImGui::GetIO(); 271 | switch (msg) 272 | { 273 | case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: 274 | case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: 275 | case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: 276 | case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: 277 | { 278 | int button = 0; 279 | if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0; } 280 | if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; } 281 | if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) { button = 2; } 282 | if (msg == WM_XBUTTONDOWN || msg == WM_XBUTTONDBLCLK) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; } 283 | if (!ImGui::IsAnyMouseDown() && ::GetCapture() == NULL) 284 | ::SetCapture(hwnd); 285 | io.MouseDown[button] = true; 286 | return 0; 287 | } 288 | case WM_LBUTTONUP: 289 | case WM_RBUTTONUP: 290 | case WM_MBUTTONUP: 291 | case WM_XBUTTONUP: 292 | { 293 | int button = 0; 294 | if (msg == WM_LBUTTONUP) { button = 0; } 295 | if (msg == WM_RBUTTONUP) { button = 1; } 296 | if (msg == WM_MBUTTONUP) { button = 2; } 297 | if (msg == WM_XBUTTONUP) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; } 298 | io.MouseDown[button] = false; 299 | if (!ImGui::IsAnyMouseDown() && ::GetCapture() == hwnd) 300 | ::ReleaseCapture(); 301 | return 0; 302 | } 303 | case WM_MOUSEWHEEL: 304 | io.MouseWheel += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA; 305 | return 0; 306 | case WM_MOUSEHWHEEL: 307 | io.MouseWheelH += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA; 308 | return 0; 309 | case WM_KEYDOWN: 310 | case WM_SYSKEYDOWN: 311 | if (wParam < 256) 312 | io.KeysDown[wParam] = 1; 313 | return 0; 314 | case WM_KEYUP: 315 | case WM_SYSKEYUP: 316 | if (wParam < 256) 317 | io.KeysDown[wParam] = 0; 318 | return 0; 319 | case WM_CHAR: 320 | // You can also use ToAscii()+GetKeyboardState() to retrieve characters. 321 | io.AddInputCharacter((unsigned int)wParam); 322 | return 0; 323 | case WM_SETCURSOR: 324 | if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor()) 325 | return 1; 326 | return 0; 327 | case WM_DEVICECHANGE: 328 | if ((UINT)wParam == DBT_DEVNODES_CHANGED) 329 | g_WantUpdateHasGamepad = true; 330 | return 0; 331 | } 332 | return 0; 333 | } 334 | -------------------------------------------------------------------------------- /ControllerOverlay/imgui/imgui_impl_win32.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Platform Binding for Windows (standard windows API for 32 and 64 bits applications) 2 | // This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) 3 | 4 | // Implemented features: 5 | // [X] Platform: Clipboard support (for Win32 this is actually part of core imgui) 6 | // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. 7 | // [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE). 8 | // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. 9 | 10 | #pragma once 11 | 12 | IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd); 13 | IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown(); 14 | IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame(); 15 | 16 | // Configuration: Disable gamepad support or linking with xinput.lib 17 | #define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD 18 | #define IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT 19 | 20 | // Handler for Win32 messages, update mouse/keyboard data. 21 | // You may or not need this for your implementation, but it can serve as reference for handling inputs. 22 | // Intentionally commented out to avoid dragging dependencies on types. You can COPY this line into your .cpp code instead. 23 | #if 0 24 | IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); 25 | #endif -------------------------------------------------------------------------------- /ControllerOverlay/imgui/imgui_rangeslider.cpp: -------------------------------------------------------------------------------- 1 | #include "imgui_rangeslider.h" 2 | #include "imgui_internal.h" 3 | #include 4 | // https://github.com/ocornut/imgui/issues/76 5 | // Inspired by: https://github.com/wasikuss/imgui/commit/a50515ace6d9a62ebcd69817f1da927d31c39bb1 6 | // Rewritten for ImGUi 1.75 7 | 8 | namespace ImGui 9 | { 10 | //------------------------------------------------------------------------- 11 | // Data 12 | //------------------------------------------------------------------------- 13 | 14 | // Those MIN/MAX values are not define because we need to point to them 15 | static const signed char IM_S8_MIN = -128; 16 | static const signed char IM_S8_MAX = 127; 17 | static const unsigned char IM_U8_MIN = 0; 18 | static const unsigned char IM_U8_MAX = 0xFF; 19 | static const signed short IM_S16_MIN = -32768; 20 | static const signed short IM_S16_MAX = 32767; 21 | static const unsigned short IM_U16_MIN = 0; 22 | static const unsigned short IM_U16_MAX = 0xFFFF; 23 | static const ImS32 IM_S32_MIN = INT_MIN; // (-2147483647 - 1), (0x80000000); 24 | static const ImS32 IM_S32_MAX = INT_MAX; // (2147483647), (0x7FFFFFFF) 25 | static const ImU32 IM_U32_MIN = 0; 26 | static const ImU32 IM_U32_MAX = UINT_MAX; // (0xFFFFFFFF) 27 | #ifdef LLONG_MIN 28 | static const ImS64 IM_S64_MIN = LLONG_MIN; // (-9223372036854775807ll - 1ll); 29 | static const ImS64 IM_S64_MAX = LLONG_MAX; // (9223372036854775807ll); 30 | #else 31 | static const ImS64 IM_S64_MIN = -9223372036854775807LL - 1; 32 | static const ImS64 IM_S64_MAX = 9223372036854775807LL; 33 | #endif 34 | static const ImU64 IM_U64_MIN = 0; 35 | #ifdef ULLONG_MAX 36 | static const ImU64 IM_U64_MAX = ULLONG_MAX; // (0xFFFFFFFFFFFFFFFFull); 37 | #else 38 | static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1); 39 | #endif 40 | 41 | //------------------------------------------------------------------------- 42 | // [SECTION] Data Type and Data Formatting Helpers [Internal] 43 | //------------------------------------------------------------------------- 44 | // - PatchFormatStringFloatToInt() 45 | // - DataTypeFormatString() 46 | //------------------------------------------------------------------------- 47 | 48 | static const ImGuiDataTypeInfo GDataTypeInfo[] = 49 | { 50 | { sizeof(char), "%d", "%d" }, // ImGuiDataType_S8 51 | { sizeof(unsigned char), "%u", "%u" }, 52 | { sizeof(short), "%d", "%d" }, // ImGuiDataType_S16 53 | { sizeof(unsigned short), "%u", "%u" }, 54 | { sizeof(int), "%d", "%d" }, // ImGuiDataType_S32 55 | { sizeof(unsigned int), "%u", "%u" }, 56 | #ifdef _MSC_VER 57 | { sizeof(ImS64), "%I64d","%I64d" }, // ImGuiDataType_S64 58 | { sizeof(ImU64), "%I64u","%I64u" }, 59 | #else 60 | { sizeof(ImS64), "%lld", "%lld" }, // ImGuiDataType_S64 61 | { sizeof(ImU64), "%llu", "%llu" }, 62 | #endif 63 | { sizeof(float), "%f", "%f" }, // ImGuiDataType_Float (float are promoted to double in va_arg) 64 | { sizeof(double), "%f", "%lf" }, // ImGuiDataType_Double 65 | }; 66 | IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT); 67 | 68 | // ~65% common code with PatchFormatStringFloatToInt. 69 | // FIXME-LEGACY: Prior to 1.61 our DragInt() function internally used floats and because of this the compile-time default value for format was "%.0f". 70 | // Even though we changed the compile-time default, we expect users to have carried %f around, which would break the display of DragInt() calls. 71 | // To honor backward compatibility we are rewriting the format string, unless IMGUI_DISABLE_OBSOLETE_FUNCTIONS is enabled. What could possibly go wrong?! 72 | static const char* PatchFormatStringFloatToInt(const char* fmt) 73 | { 74 | if (strcmp(fmt, "(%.0f, %.0f)") == 0) // Fast legacy path for "(%.0f, %.0f)" which is expected to be the most common case. 75 | return "(%d, %d)"; 76 | 77 | // Find % (if any, and ignore %%) 78 | ImGuiContext& g = *GImGui; 79 | g.TempBuffer[0] = '\0'; 80 | for (const char* fmt_b = fmt; char c = fmt_b[0]; fmt_b++) 81 | { 82 | if (c == '%' && fmt_b[1] != '%') 83 | { 84 | const char* fmt_start = fmt_b; 85 | const char* fmt_end = ImParseFormatFindEnd(fmt_start); // Find end of format specifier, which itself is an exercise of confidence/recklessness (because snprintf is dependent on libc or user). 86 | if (fmt_end > fmt_start&& fmt_end[-1] == 'f') 87 | { 88 | #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS 89 | ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%.*s%%d%s", (int)(fmt_start - fmt_b), fmt_b, fmt_end); // Honor leading and trailing decorations, but lose alignment/precision. 90 | #else 91 | IM_ASSERT(0 && "DragInt(): Invalid format string!"); // Old versions used a default parameter of "(%.0f, %.0f)", please replace with e.g. "(%d, %d)" 92 | #endif 93 | } 94 | } 95 | else if (c == '%') 96 | fmt_b++; 97 | } 98 | 99 | if (g.TempBuffer[0] == '\0') 100 | return fmt; 101 | return g.TempBuffer; 102 | } 103 | 104 | int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* p_data1, const void* p_data2, const char* format) 105 | { 106 | // Signedness doesn't matter when pushing integer arguments 107 | if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32) 108 | return ImFormatString(buf, buf_size, format, *(const ImU32*)p_data1, *(const ImU32*)p_data2); 109 | if (data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) 110 | return ImFormatString(buf, buf_size, format, *(const ImU64*)p_data1, *(const ImU64*)p_data2); 111 | if (data_type == ImGuiDataType_Float) 112 | return ImFormatString(buf, buf_size, format, *(const float*)p_data1, *(const float*)p_data2); 113 | if (data_type == ImGuiDataType_Double) 114 | return ImFormatString(buf, buf_size, format, *(const double*)p_data1, *(const double*)p_data2); 115 | if (data_type == ImGuiDataType_S8) 116 | return ImFormatString(buf, buf_size, format, *(const ImS8*)p_data1, *(const ImS8*)p_data2); 117 | if (data_type == ImGuiDataType_U8) 118 | return ImFormatString(buf, buf_size, format, *(const ImU8*)p_data1, *(const ImU8*)p_data2); 119 | if (data_type == ImGuiDataType_S16) 120 | return ImFormatString(buf, buf_size, format, *(const ImS16*)p_data1, *(const ImS16*)p_data2); 121 | if (data_type == ImGuiDataType_U16) 122 | return ImFormatString(buf, buf_size, format, *(const ImU16*)p_data1, *(const ImU16*)p_data2); 123 | IM_ASSERT(0); 124 | return 0; 125 | } 126 | 127 | //------------------------------------------------------------------------- 128 | // [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc. 129 | //------------------------------------------------------------------------- 130 | // - RangeSliderBehaviorT<>() [Internal] 131 | // - RangeSliderBehavior() [Internal] 132 | // - RangeSliderScalar() 133 | // - RangeSliderFloat() 134 | // - RangeSliderAngle() 135 | // - RangeSliderInt() 136 | //------------------------------------------------------------------------- 137 | 138 | // ~80% common code with ImGui::SliderBehaviorT. 139 | // FIXME: Move some of the code into SliderBehavior(). Current responsability is larger than what the equivalent DragBehaviorT<> does, we also do some rendering, etc. 140 | template 141 | bool RangeSliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v1, TYPE* v2, const TYPE v_min, const TYPE v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab1_bb, ImRect* out_grab2_bb) 142 | { 143 | ImGuiContext& g = *GImGui; 144 | const ImGuiStyle& style = g.Style; 145 | 146 | const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X; 147 | const bool is_decimal = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); 148 | const bool is_power = (power != 1.0f) && is_decimal; 149 | 150 | const float grab_padding = 2.0f; 151 | const float slider_sz = (bb.Max[axis] - bb.Min[axis]) - grab_padding * 2.0f; 152 | float grab_sz = style.GrabMinSize; 153 | SIGNEDTYPE v_range = (v_min < v_max ? v_max - v_min : v_min - v_max); 154 | if (!is_decimal && v_range >= 0) // v_range < 0 may happen on integer overflows 155 | grab_sz = ImMax((float)(slider_sz / (v_range + 1)), style.GrabMinSize); // For integer sliders: if possible have the grab size represent 1 unit 156 | grab_sz = ImMin(grab_sz, slider_sz); 157 | const float slider_usable_sz = slider_sz - grab_sz; 158 | const float slider_usable_pos_min = bb.Min[axis] + grab_padding + grab_sz * 0.5f; 159 | const float slider_usable_pos_max = bb.Max[axis] - grab_padding - grab_sz * 0.5f; 160 | 161 | // For power curve sliders that cross over sign boundary we want the curve to be symmetric around 0.0f 162 | float linear_zero_pos; // 0.0->1.0f 163 | if (is_power && v_min * v_max < 0.0f) 164 | { 165 | // Different sign 166 | const FLOATTYPE linear_dist_min_to_0 = ImPow(v_min >= 0 ? (FLOATTYPE)v_min : -(FLOATTYPE)v_min, (FLOATTYPE)1.0f / power); 167 | const FLOATTYPE linear_dist_max_to_0 = ImPow(v_max >= 0 ? (FLOATTYPE)v_max : -(FLOATTYPE)v_max, (FLOATTYPE)1.0f / power); 168 | linear_zero_pos = (float)(linear_dist_min_to_0 / (linear_dist_min_to_0 + linear_dist_max_to_0)); 169 | } 170 | else 171 | { 172 | // Same sign 173 | linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f; 174 | } 175 | 176 | // Process interacting with the slider 177 | bool value_changed = false; 178 | if (g.ActiveId == id) 179 | { 180 | bool set_v_new = false; 181 | static int lastDrag = -1; 182 | float clicked_t = 0.0f; 183 | if (g.ActiveIdSource == ImGuiInputSource_Mouse) 184 | { 185 | if (!g.IO.MouseDown[0]) 186 | { 187 | ClearActiveID(); 188 | } 189 | else 190 | { 191 | const float mouse_abs_pos = g.IO.MousePos[axis]; 192 | clicked_t = (slider_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f) : 0.0f; 193 | if (axis == ImGuiAxis_Y) 194 | clicked_t = 1.0f - clicked_t; 195 | set_v_new = true; 196 | } 197 | } 198 | else if (g.ActiveIdSource == ImGuiInputSource_Nav) 199 | { 200 | const ImVec2 delta2 = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 0.0f, 0.0f); 201 | float delta = (axis == ImGuiAxis_X) ? delta2.x : -delta2.y; 202 | if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) 203 | { 204 | ClearActiveID(); 205 | } 206 | else if (delta != 0.0f) 207 | { 208 | clicked_t = SliderCalcRatioFromValueT(data_type, *v1, v_min, v_max, power, linear_zero_pos); 209 | const int decimal_precision = is_decimal ? ImParseFormatPrecision(format, 3) : 0; 210 | if ((decimal_precision > 0) || is_power) 211 | { 212 | delta /= 100.0f; // Gamepad/keyboard tweak speeds in % of slider bounds 213 | if (IsNavInputDown(ImGuiNavInput_TweakSlow)) 214 | delta /= 10.0f; 215 | } 216 | else 217 | { 218 | if ((v_range >= -100.0f && v_range <= 100.0f) || IsNavInputDown(ImGuiNavInput_TweakSlow)) 219 | delta = ((delta < 0.0f) ? -1.0f : +1.0f) / (float)v_range; // Gamepad/keyboard tweak speeds in integer steps 220 | else 221 | delta /= 100.0f; 222 | } 223 | if (IsNavInputDown(ImGuiNavInput_TweakFast)) 224 | delta *= 10.0f; 225 | set_v_new = true; 226 | if ((clicked_t >= 1.0f && delta > 0.0f) || (clicked_t <= 0.0f && delta < 0.0f)) // This is to avoid applying the saturation when already past the limits 227 | set_v_new = false; 228 | else 229 | clicked_t = ImSaturate(clicked_t + delta); 230 | } 231 | } 232 | 233 | if (set_v_new) 234 | { 235 | TYPE v_new; 236 | if (is_power) 237 | { 238 | // Account for power curve scale on both sides of the zero 239 | if (clicked_t < linear_zero_pos) 240 | { 241 | // Negative: rescale to the negative range before powering 242 | float a = 1.0f - (clicked_t / linear_zero_pos); 243 | a = ImPow(a, power); 244 | v_new = ImLerp(ImMin(v_max, (TYPE)0), v_min, a); 245 | } 246 | else 247 | { 248 | // Positive: rescale to the positive range before powering 249 | float a; 250 | if (ImFabs(linear_zero_pos - 1.0f) > 1.e-6f) 251 | a = (clicked_t - linear_zero_pos) / (1.0f - linear_zero_pos); 252 | else 253 | a = clicked_t; 254 | a = ImPow(a, power); 255 | v_new = ImLerp(ImMax(v_min, (TYPE)0), v_max, a); 256 | } 257 | } 258 | else 259 | { 260 | // Linear slider 261 | if (is_decimal) 262 | { 263 | v_new = ImLerp(v_min, v_max, clicked_t); 264 | } 265 | else 266 | { 267 | // For integer values we want the clicking position to match the grab box so we round above 268 | // This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property.. 269 | FLOATTYPE v_new_off_f = (v_max - v_min) * clicked_t; 270 | TYPE v_new_off_floor = (TYPE)(v_new_off_f); 271 | TYPE v_new_off_round = (TYPE)(v_new_off_f + (FLOATTYPE)0.5); 272 | if (v_new_off_floor < v_new_off_round) 273 | v_new = v_min + v_new_off_round; 274 | else 275 | v_new = v_min + v_new_off_floor; 276 | } 277 | } 278 | 279 | // Round to user desired precision based on format string 280 | v_new = RoundScalarWithFormatT(format, data_type, v_new); 281 | 282 | // Apply result 283 | if (*v1 != v_new || *v2 != v_new) 284 | { 285 | if (lastDrag == 1 || lastDrag == 2) 286 | { 287 | if (*v2 == v_new) 288 | { 289 | *v1 = v_new; 290 | lastDrag = 2; //left to right, just hit same spot 291 | } 292 | } 293 | else if (lastDrag == 0 || lastDrag == 3) 294 | { 295 | if (*v1 == v_new) 296 | { 297 | *v2 = v_new; 298 | lastDrag = 3; //right to left, just hit left spot 299 | } 300 | } 301 | if (ImFabs(*v1 - v_new) == ImFabs(*v2 - v_new)) 302 | { 303 | if (*v1 == *v2) 304 | { 305 | if (*v1 != v_new) 306 | { 307 | if (v_new > * v1) 308 | { 309 | *v2 = v_new; 310 | } 311 | else 312 | { 313 | *v1 = v_new; 314 | } 315 | } 316 | } 317 | //else 318 | if (lastDrag == 1) 319 | { 320 | *v1 = v_new; 321 | } 322 | else if (lastDrag == 0) 323 | { 324 | *v2 = v_new; 325 | } 326 | 327 | } 328 | else if (ImFabs(*v1 - v_new) < ImFabs(*v2 - v_new)) 329 | { 330 | *v1 = v_new; 331 | lastDrag = 1; 332 | } 333 | else 334 | { 335 | *v2 = v_new; 336 | lastDrag = 0; 337 | } 338 | value_changed = true; 339 | } 340 | } 341 | } 342 | 343 | if (slider_sz < 1.0f) 344 | { 345 | *out_grab1_bb = ImRect(bb.Min, bb.Min); 346 | *out_grab2_bb = ImRect(bb.Min, bb.Min); 347 | } 348 | else 349 | { 350 | // Output grab1 position so it can be displayed by the caller 351 | float grab1_t = SliderCalcRatioFromValueT(data_type, *v1, v_min, v_max, power, linear_zero_pos); 352 | if (axis == ImGuiAxis_Y) 353 | grab1_t = 1.0f - grab1_t; 354 | const float grab1_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab1_t); 355 | if (axis == ImGuiAxis_X) 356 | *out_grab1_bb = ImRect(grab1_pos - grab_sz * 0.5f, bb.Min.y + grab_padding, grab1_pos + grab_sz * 0.5f, bb.Max.y - grab_padding); 357 | else 358 | *out_grab1_bb = ImRect(bb.Min.x + grab_padding, grab1_pos - grab_sz * 0.5f, bb.Max.x - grab_padding, grab1_pos + grab_sz * 0.5f); 359 | 360 | // Output grab2 position so it can be displayed by the caller 361 | float grab2_t = SliderCalcRatioFromValueT(data_type, *v2, v_min, v_max, power, linear_zero_pos); 362 | if (axis == ImGuiAxis_Y) 363 | grab2_t = 1.0f - grab2_t; 364 | const float grab2_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab2_t); 365 | if (axis == ImGuiAxis_X) 366 | *out_grab2_bb = ImRect(grab2_pos - grab_sz * 0.5f, bb.Min.y + grab_padding, grab2_pos + grab_sz * 0.5f, bb.Max.y - grab_padding); 367 | else 368 | *out_grab2_bb = ImRect(bb.Min.x + grab_padding, grab2_pos - grab_sz * 0.5f, bb.Max.x - grab_padding, grab2_pos + grab_sz * 0.5f); 369 | } 370 | 371 | return value_changed; 372 | } 373 | 374 | // ~95% common code with ImGui::SliderBehavior. 375 | // For 32-bit and larger types, slider bounds are limited to half the natural type range. 376 | // So e.g. an integer Slider between INT_MAX-10 and INT_MAX will fail, but an integer Slider between INT_MAX/2-10 and INT_MAX/2 will be ok. 377 | // It would be possible to lift that limitation with some work but it doesn't seem to be worth it for sliders. 378 | bool RangeSliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v1, void* p_v2, const void* p_min, const void* p_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab1_bb, ImRect* out_grab2_bb) 379 | { 380 | switch (data_type) 381 | { 382 | case ImGuiDataType_S8: 383 | { 384 | ImS32 v32_1 = (ImS32) * (ImS8*)p_v1; 385 | ImS32 v32_2 = (ImS32) * (ImS8*)p_v2; 386 | bool r = RangeSliderBehaviorT(bb, id, ImGuiDataType_S32, &v32_1, &v32_2, *(const ImS8*)p_min, *(const ImS8*)p_max, format, power, flags, out_grab1_bb, out_grab2_bb); 387 | if (r) 388 | { 389 | *(ImS8*)p_v1 = (ImS8)v32_1; 390 | *(ImS8*)p_v2 = (ImS8)v32_2; 391 | } 392 | return r; 393 | } 394 | case ImGuiDataType_U8: 395 | { 396 | ImU32 v32_1 = (ImU32) * (ImU8*)p_v1; 397 | ImU32 v32_2 = (ImU32) * (ImU8*)p_v2; 398 | bool r = RangeSliderBehaviorT(bb, id, ImGuiDataType_U32, &v32_1, &v32_2, *(const ImU8*)p_min, *(const ImU8*)p_max, format, power, flags, out_grab1_bb, out_grab2_bb); 399 | if (r) 400 | { 401 | *(ImU8*)p_v1 = (ImU8)v32_1; 402 | *(ImU8*)p_v2 = (ImU8)v32_2; 403 | } 404 | return r; 405 | } 406 | case ImGuiDataType_S16: 407 | { 408 | ImS32 v32_1 = (ImS32) * (ImS16*)p_v1; 409 | ImS32 v32_2 = (ImS32) * (ImS16*)p_v2; 410 | bool r = RangeSliderBehaviorT(bb, id, ImGuiDataType_S32, &v32_1, &v32_2, *(const ImS16*)p_min, *(const ImS16*)p_max, format, power, flags, out_grab1_bb, out_grab2_bb); 411 | if (r) 412 | { 413 | *(ImS16*)p_v1 = (ImS16)v32_1; 414 | *(ImS16*)p_v2 = (ImS16)v32_2; 415 | } 416 | return r; 417 | } 418 | case ImGuiDataType_U16: 419 | { 420 | ImU32 v32_1 = (ImU32) * (ImU16*)p_v1; 421 | ImU32 v32_2 = (ImU32) * (ImU16*)p_v2; 422 | bool r = RangeSliderBehaviorT(bb, id, ImGuiDataType_U32, &v32_1, &v32_2, *(const ImU16*)p_min, *(const ImU16*)p_max, format, power, flags, out_grab1_bb, out_grab2_bb); 423 | if (r) 424 | { 425 | *(ImU16*)p_v1 = (ImU16)v32_1; 426 | *(ImU16*)p_v2 = (ImU16)v32_2; 427 | } 428 | return r; 429 | } 430 | case ImGuiDataType_S32: 431 | IM_ASSERT(*(const ImS32*)p_min >= IM_S32_MIN / 2 && *(const ImS32*)p_max <= IM_S32_MAX / 2); 432 | return RangeSliderBehaviorT(bb, id, data_type, (ImS32*)p_v1, (ImS32*)p_v2, *(const ImS32*)p_min, *(const ImS32*)p_max, format, power, flags, out_grab1_bb, out_grab2_bb); 433 | case ImGuiDataType_U32: 434 | IM_ASSERT(*(const ImU32*)p_max <= IM_U32_MAX / 2); 435 | return RangeSliderBehaviorT(bb, id, data_type, (ImU32*)p_v1, (ImU32*)p_v2, *(const ImU32*)p_min, *(const ImU32*)p_max, format, power, flags, out_grab1_bb, out_grab2_bb); 436 | case ImGuiDataType_S64: 437 | IM_ASSERT(*(const ImS64*)p_min >= IM_S64_MIN / 2 && *(const ImS64*)p_max <= IM_S64_MAX / 2); 438 | return RangeSliderBehaviorT(bb, id, data_type, (ImS64*)p_v1, (ImS64*)p_v2, *(const ImS64*)p_min, *(const ImS64*)p_max, format, power, flags, out_grab1_bb, out_grab2_bb); 439 | case ImGuiDataType_U64: 440 | IM_ASSERT(*(const ImU64*)p_max <= IM_U64_MAX / 2); 441 | return RangeSliderBehaviorT(bb, id, data_type, (ImU64*)p_v1, (ImU64*)p_v2, *(const ImU64*)p_min, *(const ImU64*)p_max, format, power, flags, out_grab1_bb, out_grab2_bb); 442 | case ImGuiDataType_Float: 443 | IM_ASSERT(*(const float*)p_min >= -FLT_MAX / 2.0f && *(const float*)p_max <= FLT_MAX / 2.0f); 444 | return RangeSliderBehaviorT(bb, id, data_type, (float*)p_v1, (float*)p_v2, *(const float*)p_min, *(const float*)p_max, format, power, flags, out_grab1_bb, out_grab2_bb); 445 | case ImGuiDataType_Double: 446 | IM_ASSERT(*(const double*)p_min >= -DBL_MAX / 2.0f && *(const double*)p_max <= DBL_MAX / 2.0f); 447 | return RangeSliderBehaviorT(bb, id, data_type, (double*)p_v1, (double*)p_v2, *(const double*)p_min, *(const double*)p_max, format, power, flags, out_grab1_bb, out_grab2_bb); 448 | case ImGuiDataType_COUNT: break; 449 | } 450 | IM_ASSERT(0); 451 | return false; 452 | } 453 | 454 | // ~95% common code with ImGui::SliderScalar 455 | // Note: p_data, p_min and p_max are _pointers_ to a memory address holding the data. For a slider, they are all required. 456 | // Read code of e.g. SliderFloat(), SliderInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly. 457 | bool ImGui::RangeSliderScalar(const char* label, ImGuiDataType data_type, void* p_data1, void* p_data2, const void* p_min, const void* p_max, const char* format, float power) 458 | { 459 | ImGuiWindow* window = GetCurrentWindow(); 460 | if (window->SkipItems) 461 | return false; 462 | 463 | ImGuiContext& g = *GImGui; 464 | const ImGuiStyle& style = g.Style; 465 | const ImGuiID id = window->GetID(label); 466 | const float w = CalcItemWidth(); 467 | 468 | const ImVec2 label_size = CalcTextSize(label, NULL, true); 469 | const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); 470 | const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); 471 | 472 | ItemSize(total_bb, style.FramePadding.y); 473 | if (!ItemAdd(total_bb, id, &frame_bb)) 474 | return false; 475 | 476 | // Default format string when passing NULL 477 | if (format == NULL) 478 | format = (std::string("(") + DataTypeGetInfo(data_type)->PrintFmt + ", " + DataTypeGetInfo(data_type)->PrintFmt + ")").c_str(); 479 | else if (data_type == ImGuiDataType_S32 && strcmp(format, "(%d, %d)") != 0) // (FIXME-LEGACY: Patch old "%.0f" format string to use "%d", read function more details.) 480 | format = PatchFormatStringFloatToInt(format); 481 | 482 | // Tabbing or CTRL-clicking on Slider turns it into an input box 483 | const bool hovered = ItemHoverable(frame_bb, id); 484 | bool temp_input_is_active = TempInputTextIsActive(id); 485 | bool temp_input_start = false; 486 | if (!temp_input_is_active) 487 | { 488 | //const bool focus_requested = FocusableItemRegister(window, id); 489 | const bool focus_requested = FocusableItemRegister(window, g.ActiveId == id); 490 | const bool clicked = (hovered && g.IO.MouseClicked[0]); 491 | if (focus_requested || clicked || g.NavActivateId == id || g.NavInputId == id) 492 | { 493 | SetActiveID(id, window); 494 | SetFocusID(id, window); 495 | FocusWindow(window); 496 | g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); 497 | if (focus_requested || (clicked && g.IO.KeyCtrl) || g.NavInputId == id) 498 | { 499 | temp_input_start = true; 500 | FocusableItemUnregister(window); 501 | } 502 | } 503 | } 504 | if (temp_input_is_active || temp_input_start) 505 | return TempInputTextScalar(frame_bb, id, label, data_type, p_data1, format); 506 | 507 | // Draw frame 508 | const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); 509 | RenderNavHighlight(frame_bb, id); 510 | RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); 511 | 512 | // Slider behavior 513 | ImRect grab_bb1, grab_bb2; 514 | const bool value_changed = RangeSliderBehavior(frame_bb, id, data_type, p_data1, p_data2, p_min, p_max, format, power, ImGuiSliderFlags_None, &grab_bb1, &grab_bb2); 515 | if (value_changed) 516 | MarkItemEdited(id); 517 | 518 | // Render grabs 519 | if (grab_bb1.Max.x > grab_bb1.Min.x) 520 | window->DrawList->AddRectFilled(grab_bb1.Min, grab_bb1.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); 521 | if (grab_bb2.Max.x > grab_bb2.Min.x) 522 | window->DrawList->AddRectFilled(grab_bb2.Min, grab_bb2.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); 523 | // Render connector 524 | ImRect connector(grab_bb1.Min, grab_bb2.Max); 525 | float grab_sz = grab_bb1.Max.x - grab_bb1.Min.x; 526 | connector.Min.x += grab_sz; 527 | connector.Min.y += grab_sz * 0.3f; 528 | connector.Max.x -= grab_sz; 529 | connector.Max.y -= grab_sz * 0.3f; 530 | window->DrawList->AddRectFilled(connector.Min, connector.Max, GetColorU32(ImGuiCol_RangeSliderBar), style.GrabRounding); 531 | 532 | // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. 533 | char value_buf[64]; 534 | const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data1, p_data2, format); 535 | RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f)); 536 | 537 | if (label_size.x > 0.0f) 538 | RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); 539 | 540 | IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); 541 | return value_changed; 542 | } 543 | 544 | // ~95% common code with ImGui::SliderScalarN 545 | // Add multiple sliders on 1 line for compact edition of multiple components 546 | bool ImGui::RangeSliderScalarN(const char* label, ImGuiDataType data_type, void* v1, void* v2, int components, const void* v_min, const void* v_max, const char* format, float power) 547 | { 548 | ImGuiWindow* window = GetCurrentWindow(); 549 | if (window->SkipItems) 550 | return false; 551 | 552 | ImGuiContext& g = *GImGui; 553 | bool value_changed = false; 554 | BeginGroup(); 555 | PushID(label); 556 | PushMultiItemsWidths(components, CalcItemWidth()); 557 | size_t type_size = GDataTypeInfo[data_type].Size; 558 | for (int i = 0; i < components; i++) 559 | { 560 | PushID(i); 561 | if (i > 0) 562 | SameLine(0, g.Style.ItemInnerSpacing.x); 563 | value_changed |= RangeSliderScalar("", data_type, v1, v2, v_min, v_max, format, power); 564 | PopID(); 565 | PopItemWidth(); 566 | v1 = (void*)((char*)v1 + type_size); 567 | v2 = (void*)((char*)v2 + type_size); 568 | } 569 | PopID(); 570 | 571 | const char* label_end = FindRenderedTextEnd(label); 572 | if (label != label_end) 573 | { 574 | SameLine(0, g.Style.ItemInnerSpacing.x); 575 | TextEx(label, label_end); 576 | } 577 | 578 | EndGroup(); 579 | return value_changed; 580 | } 581 | 582 | // ~95% common code with ImGui::SliderFloat 583 | bool ImGui::RangeSliderFloat(const char* label, float* v1, float* v2, float v_min, float v_max, const char* format, float power) 584 | { 585 | return RangeSliderScalar(label, ImGuiDataType_Float, v1, v2, &v_min, &v_max, format, power); 586 | } 587 | 588 | // ~95% common code with ImGui::SliderFloat2 589 | bool ImGui::RangeSliderFloat2(const char* label, float v1[2], float v2[2], float v_min, float v_max, const char* format, float power) 590 | { 591 | return RangeSliderScalarN(label, ImGuiDataType_Float, v1, v2, 2, &v_min, &v_max, format, power); 592 | } 593 | 594 | // ~95% common code with ImGui::SliderFloat3 595 | bool ImGui::RangeSliderFloat3(const char* label, float v1[3], float v2[3], float v_min, float v_max, const char* format, float power) 596 | { 597 | return RangeSliderScalarN(label, ImGuiDataType_Float, v1, v2, 3, &v_min, &v_max, format, power); 598 | } 599 | 600 | // ~95% common code with ImGui::SliderFloat4 601 | bool ImGui::RangeSliderFloat4(const char* label, float v1[4], float v2[4], float v_min, float v_max, const char* format, float power) 602 | { 603 | return RangeSliderScalarN(label, ImGuiDataType_Float, v1, v2, 4, &v_min, &v_max, format, power); 604 | } 605 | 606 | // ~95% common code with ImGui::SliderAngle 607 | bool ImGui::RangeSliderAngle(const char* label, float* v_rad1, float* v_rad2, float v_degrees_min, float v_degrees_max, const char* format) 608 | { 609 | if (format == NULL) 610 | format = "%d deg"; 611 | float v_deg1 = (*v_rad1) * 360.0f / (2 * IM_PI); 612 | float v_deg2 = (*v_rad2) * 360.0f / (2 * IM_PI); 613 | bool value_changed = RangeSliderFloat(label, &v_deg2, &v_deg1, v_degrees_min, v_degrees_max, format, 1.0f); 614 | *v_rad1 = v_deg1 * (2 * IM_PI) / 360.0f; 615 | *v_rad2 = v_deg2 * (2 * IM_PI) / 360.0f; 616 | return value_changed; 617 | } 618 | 619 | // ~95% common code with ImGui::SliderInt 620 | bool ImGui::RangeSliderInt(const char* label, int* v1, int* v2, int v_min, int v_max, const char* format) 621 | { 622 | return RangeSliderScalar(label, ImGuiDataType_S32, v1, v2, &v_min, &v_max, format); 623 | } 624 | 625 | // ~95% common code with ImGui::SliderInt2 626 | bool ImGui::RangeSliderInt2(const char* label, int v1[2], int v2[2], int v_min, int v_max, const char* format) 627 | { 628 | return RangeSliderScalarN(label, ImGuiDataType_S32, v1, v2, 2, &v_min, &v_max, format); 629 | } 630 | 631 | // ~95% common code with ImGui::SliderInt3 632 | bool ImGui::RangeSliderInt3(const char* label, int v1[3], int v2[3], int v_min, int v_max, const char* format) 633 | { 634 | return RangeSliderScalarN(label, ImGuiDataType_S32, v1, v2, 3, &v_min, &v_max, format); 635 | } 636 | 637 | // ~95% common code with ImGui::SliderInt4 638 | bool ImGui::RangeSliderInt4(const char* label, int v1[4], int v2[4], int v_min, int v_max, const char* format) 639 | { 640 | return RangeSliderScalarN(label, ImGuiDataType_S32, v1, v2, 4, &v_min, &v_max, format); 641 | } 642 | 643 | bool ImGui::RangeVSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data1, void* p_data2, const void* p_min, const void* p_max, const char* format, float power) 644 | { 645 | ImGuiWindow* window = GetCurrentWindow(); 646 | if (window->SkipItems) 647 | return false; 648 | 649 | ImGuiContext& g = *GImGui; 650 | const ImGuiStyle& style = g.Style; 651 | const ImGuiID id = window->GetID(label); 652 | 653 | const ImVec2 label_size = CalcTextSize(label, NULL, true); 654 | const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size); 655 | const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); 656 | 657 | ItemSize(bb, style.FramePadding.y); 658 | if (!ItemAdd(frame_bb, id)) 659 | return false; 660 | 661 | // Default format string when passing NULL 662 | if (format == NULL) 663 | format = DataTypeGetInfo(data_type)->PrintFmt; 664 | else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) // (FIXME-LEGACY: Patch old "%.0f" format string to use "%d", read function more details.) 665 | format = PatchFormatStringFloatToInt(format); 666 | 667 | const bool hovered = ItemHoverable(frame_bb, id); 668 | if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id) 669 | { 670 | SetActiveID(id, window); 671 | SetFocusID(id, window); 672 | FocusWindow(window); 673 | g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); 674 | } 675 | 676 | // Draw frame 677 | const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); 678 | RenderNavHighlight(frame_bb, id); 679 | RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); 680 | 681 | // Slider behavior 682 | ImRect grab_bb1, grab_bb2; 683 | const bool value_changed = RangeSliderBehavior(frame_bb, id, data_type, p_data1, p_data2, p_min, p_max, format, power, ImGuiSliderFlags_Vertical, &grab_bb1, &grab_bb2); 684 | if (value_changed) 685 | MarkItemEdited(id); 686 | 687 | // Render grabs 688 | if (grab_bb1.Max.y > grab_bb1.Min.y) 689 | window->DrawList->AddRectFilled(grab_bb1.Min, grab_bb1.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); 690 | if (grab_bb2.Max.y > grab_bb2.Min.y) 691 | window->DrawList->AddRectFilled(grab_bb2.Min, grab_bb2.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); 692 | // Render connector 693 | ImRect connector(grab_bb1.Min, grab_bb2.Max); 694 | float grab_sz = grab_bb1.Max.x - grab_bb1.Min.x; 695 | connector.Min.x += grab_sz; 696 | connector.Min.y += grab_sz * 0.3f; 697 | connector.Max.x -= grab_sz; 698 | connector.Max.y -= grab_sz * 0.3f; 699 | window->DrawList->AddRectFilled(connector.Min, connector.Max, GetColorU32(ImGuiCol_RangeSliderBar), style.GrabRounding); 700 | 701 | // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. 702 | // For the vertical slider we allow centered text to overlap the frame padding 703 | char value_buf[64]; 704 | const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data1, p_data2, format); 705 | RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.0f)); 706 | if (label_size.x > 0.0f) 707 | RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); 708 | 709 | return value_changed; 710 | } 711 | 712 | bool ImGui::RangeVSliderFloat(const char* label, const ImVec2& size, float* v1, float* v2, float v_min, float v_max, const char* format, float power) 713 | { 714 | return RangeVSliderScalar(label, size, ImGuiDataType_Float, v1, v2, &v_min, &v_max, format, power); 715 | } 716 | 717 | bool ImGui::RangeVSliderInt(const char* label, const ImVec2& size, int* v1, int* v2, int v_min, int v_max, const char* format) 718 | { 719 | return RangeVSliderScalar(label, size, ImGuiDataType_S32, v1, v2, &v_min, &v_max, format); 720 | } 721 | } // namespace ImGui -------------------------------------------------------------------------------- /ControllerOverlay/imgui/imgui_rangeslider.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "imgui.h" 3 | namespace ImGui 4 | { 5 | IMGUI_API bool RangeSliderScalar(const char* label, ImGuiDataType data_type, void* p_data1, void* p_data2, const void* p_min, const void* p_max, const char* format = NULL, float power = 1.0f); 6 | IMGUI_API bool RangeSliderScalarN(const char* label, ImGuiDataType data_type, void* p_data1, void* p_data2, int components, const void* p_min, const void* p_max, const char* format = NULL, float power = 1.0f); 7 | IMGUI_API bool RangeSliderFloat(const char* label, float* v1, float* v2, float v_min, float v_max, const char* format = "(%.3f, %.3f)", float power = 1.0f); // adjust format to decorate the value with a prefix or a suffix for in-slider labels or unit display. Use power!=1.0 for power curve sliders 8 | IMGUI_API bool RangeSliderFloat2(const char* label, float v1[2], float v2[2], float v_min, float v_max, const char* format = "(%.3f, %.3f)", float power = 1.0f); 9 | IMGUI_API bool RangeSliderFloat3(const char* label, float v1[3], float v2[3], float v_min, float v_max, const char* format = "(%.3f, %.3f)", float power = 1.0f); 10 | IMGUI_API bool RangeSliderFloat4(const char* label, float v1[4], float v2[4], float v_min, float v_max, const char* format = "(%.3f, %.3f)", float power = 1.0f); 11 | IMGUI_API bool RangeSliderAngle(const char* label, float* v_rad1, float* v_rad2, float v_degrees_min = -360.0f, float v_degrees_max = +360.0f, const char* format = "(%d, %d) deg"); 12 | IMGUI_API bool RangeSliderInt(const char* label, int* v1, int* v2, int v_min, int v_max, const char* format = "(%d, %d)"); 13 | IMGUI_API bool RangeSliderInt2(const char* label, int v1[2], int v2[2], int v_min, int v_max, const char* format = "(%d, %d)"); 14 | IMGUI_API bool RangeSliderInt3(const char* label, int v1[3], int v2[3], int v_min, int v_max, const char* format = "(%d, %d)"); 15 | IMGUI_API bool RangeSliderInt4(const char* label, int v1[4], int v2[4], int v_min, int v_max, const char* format = "(%d, %d)"); 16 | IMGUI_API bool RangeVSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data1, void* p_data2, const void* p_min, const void* p_max, const char* format = NULL, float power = 1.0f); 17 | IMGUI_API bool RangeVSliderFloat(const char* label, const ImVec2& size, float* v1, float* v2, float v_min, float v_max, const char* format = "(%.3f, %.3f)", float power = 1.0f); 18 | IMGUI_API bool RangeVSliderInt(const char* label, const ImVec2& size, int* v1, int* v2, int v_min, int v_max, const char* format = "(%d, %d)"); 19 | 20 | } // namespace ImGui -------------------------------------------------------------------------------- /ControllerOverlay/imgui/imgui_searchablecombo.cpp: -------------------------------------------------------------------------------- 1 | #include "imgui_searchablecombo.h" 2 | #include "imgui_internal.h" 3 | 4 | static float CalcMaxPopupHeightFromItemCount(int items_count) 5 | { 6 | ImGuiContext& g = *GImGui; 7 | if (items_count <= 0) 8 | return FLT_MAX; 9 | return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2); 10 | } 11 | 12 | /* Modified version of BeginCombo from imgui.cpp at line 9172, 13 | * to include a input field to be able to filter the combo values. */ 14 | bool ImGui::BeginSearchableCombo(const char* label, const char* preview_value, char* input, int input_size, const char* input_preview_value, ImGuiComboFlags flags) 15 | { 16 | // Always consume the SetNextWindowSizeConstraint() call in our early return paths 17 | ImGuiContext& g = *GImGui; 18 | bool has_window_size_constraint = (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint) != 0; 19 | g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; 20 | 21 | ImGuiWindow* window = GetCurrentWindow(); 22 | if (window->SkipItems) 23 | return false; 24 | 25 | IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together 26 | 27 | const ImGuiStyle& style = g.Style; 28 | const ImGuiID id = window->GetID(label); 29 | 30 | const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight(); 31 | const ImVec2 label_size = CalcTextSize(label, NULL, true); 32 | const float expected_w = CalcItemWidth(); 33 | const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : expected_w; 34 | const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); 35 | const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); 36 | 37 | bool hovered, held; 38 | bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held); 39 | bool popup_open = IsPopupOpen(id); 40 | 41 | const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); 42 | const float value_x2 = ImMax(frame_bb.Min.x, frame_bb.Max.x - arrow_size); 43 | 44 | if (popup_open || (pressed || g.NavActivateId == id)) 45 | { 46 | ImGuiID activeId = g.ActiveId; 47 | if (popup_open) 48 | g.ActiveId = id; 49 | else 50 | g.NavInputId = id; 51 | InputTextEx(label, input_preview_value, input, input_size, frame_bb.Max - frame_bb.Min, ImGuiInputTextFlags_NoUndoRedo); 52 | g.ActiveId = activeId; 53 | if (!(flags & ImGuiComboFlags_NoArrowButton)) 54 | { 55 | ImU32 bg_col = GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button); 56 | ImU32 text_col = GetColorU32(ImGuiCol_Text); 57 | window->DrawList->AddRectFilled(ImVec2(value_x2, frame_bb.Min.y), frame_bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Right); 58 | if (value_x2 + arrow_size - style.FramePadding.x <= frame_bb.Max.x) 59 | RenderArrow(window->DrawList, ImVec2(value_x2 + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), text_col, ImGuiDir_Down, 1.0f); 60 | } 61 | } 62 | else 63 | { 64 | ItemSize(total_bb, style.FramePadding.y); 65 | if (!ItemAdd(total_bb, id, &frame_bb)) 66 | return false; 67 | } 68 | 69 | if ((pressed || g.NavActivateId == id) && !popup_open) 70 | { 71 | if (window->DC.NavLayerCurrent == 0) 72 | window->NavLastIds[0] = id; 73 | OpenPopupEx(id); 74 | popup_open = true; 75 | } 76 | 77 | if (!popup_open) 78 | { 79 | RenderNavHighlight(frame_bb, id); 80 | if (!(flags & ImGuiComboFlags_NoPreview)) 81 | window->DrawList->AddRectFilled(frame_bb.Min, ImVec2(value_x2, frame_bb.Max.y), frame_col, style.FrameRounding, (flags & ImGuiComboFlags_NoArrowButton) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Left); 82 | if (!(flags & ImGuiComboFlags_NoArrowButton)) 83 | { 84 | ImU32 bg_col = GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button); 85 | ImU32 text_col = GetColorU32(ImGuiCol_Text); 86 | window->DrawList->AddRectFilled(ImVec2(value_x2, frame_bb.Min.y), frame_bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Right); 87 | if (value_x2 + arrow_size - style.FramePadding.x <= frame_bb.Max.x) 88 | RenderArrow(window->DrawList, ImVec2(value_x2 + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), text_col, ImGuiDir_Down, 1.0f); 89 | } 90 | RenderFrameBorder(frame_bb.Min, frame_bb.Max, style.FrameRounding); 91 | 92 | if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview)) 93 | RenderTextClipped(frame_bb.Min + style.FramePadding, ImVec2(value_x2, frame_bb.Max.y), preview_value, NULL, NULL, ImVec2(0.0f, 0.0f)); 94 | if (label_size.x > 0) 95 | RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); 96 | 97 | return false; 98 | } 99 | 100 | if (has_window_size_constraint) 101 | { 102 | g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint; 103 | g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w); 104 | } 105 | else 106 | { 107 | if ((flags & ImGuiComboFlags_HeightMask_) == 0) 108 | flags |= ImGuiComboFlags_HeightRegular; 109 | IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one 110 | int popup_max_height_in_items = -1; 111 | if (flags & ImGuiComboFlags_HeightRegular) popup_max_height_in_items = 8; 112 | else if (flags & ImGuiComboFlags_HeightSmall) popup_max_height_in_items = 4; 113 | else if (flags & ImGuiComboFlags_HeightLarge) popup_max_height_in_items = 20; 114 | SetNextWindowSizeConstraints(ImVec2(w, 0.0f), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items))); 115 | } 116 | 117 | char name[16]; 118 | ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth 119 | 120 | // Peak into expected window size so we can position it 121 | if (ImGuiWindow* popup_window = FindWindowByName(name)) 122 | if (popup_window->WasActive) 123 | { 124 | ImVec2 size_expected = CalcWindowExpectedSize(popup_window); 125 | if (flags & ImGuiComboFlags_PopupAlignLeft) 126 | popup_window->AutoPosLastDirection = ImGuiDir_Left; 127 | ImRect r_outer = GetWindowAllowedExtentRect(popup_window); 128 | ImVec2 pos = FindBestWindowPosForPopupEx(frame_bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, frame_bb, ImGuiPopupPositionPolicy_ComboBox); 129 | SetNextWindowPos(pos); 130 | } 131 | 132 | // We don't use BeginPopupEx() solely because we have a custom name string, which we could make an argument to BeginPopupEx() 133 | ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove; 134 | 135 | // Horizontally align ourselves with the framed text 136 | PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(style.FramePadding.x, style.WindowPadding.y)); 137 | bool ret = Begin(name, NULL, window_flags); 138 | PopStyleVar(); 139 | if (!ret) 140 | { 141 | EndPopup(); 142 | IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above 143 | return false; 144 | } 145 | 146 | return true; 147 | } 148 | 149 | 150 | // Just so you can end your BeginSearchableCombo with EndSearchableCombo. 151 | void ImGui::EndSearchableCombo() 152 | { 153 | EndCombo(); 154 | } 155 | 156 | 157 | /* Modified version of Combo from imgui.cpp at line 9343, 158 | * to include a input field to be able to filter the combo values. */ 159 | bool ImGui::SearchableCombo(const char* label, int* current_item, std::vector items, const char* default_preview_text, const char* input_preview_value, int popup_max_height_in_items) 160 | { 161 | ImGuiContext& g = *GImGui; 162 | 163 | const char* preview_text = NULL; 164 | if (*current_item >= (int)items.size()) 165 | *current_item = 0; 166 | if (*current_item >= 0 && *current_item < (int)items.size()) 167 | preview_text = items[*current_item].c_str(); 168 | else 169 | preview_text = default_preview_text; 170 | 171 | // The old Combo() API exposed "popup_max_height_in_items". The new more general BeginCombo() API doesn't have/need it, but we emulate it here. 172 | if (popup_max_height_in_items != -1 && !(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)) 173 | SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items))); 174 | 175 | const int input_size = 64; 176 | char input_buffer[input_size] = ""; 177 | if (!BeginSearchableCombo(label, preview_text, input_buffer, input_size, input_preview_value, ImGuiComboFlags_None)) 178 | return false; 179 | 180 | // Display items 181 | // FIXME-OPT: Use clipper (but we need to disable it on the appearing frame to make sure our call to SetItemDefaultFocus() is processed) 182 | int matched_items = 0; 183 | bool value_changed = false; 184 | for (int i = 0; i < (int)items.size(); i++) 185 | { 186 | char buffer[input_size] = ""; 187 | ImStrncpy(buffer, input_buffer, input_size); 188 | std::string input(buffer); 189 | std::string item = items[i]; 190 | 191 | std::transform(item.begin(), item.end(), item.begin(), 192 | [](unsigned char c) { return (unsigned char)std::tolower(c); }); 193 | std::transform(input.begin(), input.end(), input.begin(), 194 | [](unsigned char c) { return (unsigned char)std::tolower(c); }); 195 | 196 | if (item.find(input, 0) == std::string::npos) 197 | continue; 198 | 199 | matched_items++; 200 | PushID((void*)(intptr_t)i); 201 | const bool item_selected = (i == *current_item); 202 | const char* item_text = items[i].c_str(); 203 | if (Selectable(item_text, item_selected)) 204 | { 205 | value_changed = true; 206 | *current_item = i; 207 | } 208 | if (item_selected) 209 | SetItemDefaultFocus(); 210 | PopID(); 211 | } 212 | if (matched_items == 0) 213 | ImGui::Selectable("No maps found", false, ImGuiSelectableFlags_Disabled); 214 | 215 | EndSearchableCombo(); 216 | 217 | return value_changed; 218 | } -------------------------------------------------------------------------------- /ControllerOverlay/imgui/imgui_searchablecombo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "imgui.h" 3 | 4 | #include // isprint 5 | #include // vector<> 6 | #include // string 7 | #include // transform 8 | 9 | namespace ImGui 10 | { 11 | IMGUI_API bool BeginSearchableCombo(const char* label, const char* preview_value, char* input, int input_size, const char* input_preview_value, ImGuiComboFlags flags = 0); 12 | IMGUI_API void EndSearchableCombo(); 13 | IMGUI_API bool SearchableCombo(const char* label, int* current_item, std::vector items, const char* default_preview_text, const char* input_preview_value, int popup_max_height_in_items = -1); 14 | } // namespace ImGui -------------------------------------------------------------------------------- /ControllerOverlay/imgui/imgui_timeline.cpp: -------------------------------------------------------------------------------- 1 | #include "imgui_timeline.h" 2 | // cpp 3 | #include "imgui.h" 4 | #include "imgui_internal.h" 5 | // https://github.com/ocornut/imgui/issues/76 6 | 7 | namespace ImGui { 8 | 9 | static float s_max_timeline_value; 10 | 11 | 12 | bool BeginTimeline(const char* str_id, float max_time) 13 | { 14 | s_max_timeline_value = max_time; 15 | return BeginChild(str_id); 16 | } 17 | 18 | 19 | static const float TIMELINE_RADIUS = 6; 20 | 21 | 22 | bool TimelineEvent(const char* str_id, float values[2]) 23 | { 24 | ImGuiWindow* win = GetCurrentWindow(); 25 | const ImU32 inactive_color = ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_Button]); 26 | const ImU32 active_color = ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_ButtonHovered]); 27 | const ImU32 line_color = ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_SeparatorActive]); 28 | bool changed = false; 29 | ImVec2 cursor_pos = win->DC.CursorPos; 30 | 31 | // @r-lyeh { 32 | Button(str_id, ImVec2(120, 0)); // @todo: enable/disable track channel here 33 | SameLine(); 34 | cursor_pos += ImVec2(0, GetTextLineHeightWithSpacing() / 3); 35 | // } 36 | 37 | for (int i = 0; i < 2; ++i) 38 | { 39 | ImVec2 pos = cursor_pos; 40 | pos.x += win->Size.x * values[i] / s_max_timeline_value + TIMELINE_RADIUS; 41 | pos.y += TIMELINE_RADIUS; 42 | 43 | SetCursorScreenPos(pos - ImVec2(TIMELINE_RADIUS, TIMELINE_RADIUS)); 44 | PushID(i); 45 | InvisibleButton(str_id, ImVec2(2 * TIMELINE_RADIUS, 2 * TIMELINE_RADIUS)); 46 | if (IsItemActive() || IsItemHovered()) 47 | { 48 | ImGui::SetTooltip("%f", values[i]); 49 | ImVec2 a(pos.x, GetWindowContentRegionMin().y + win->Pos.y + win->Scroll.y); 50 | ImVec2 b(pos.x, GetWindowContentRegionMax().y + win->Pos.y + win->Scroll.y); 51 | win->DrawList->AddLine(a, b, line_color); 52 | } 53 | if (IsItemActive() && IsMouseDragging(0)) 54 | { 55 | values[i] += GetIO().MouseDelta.x / win->Size.x * s_max_timeline_value; 56 | changed = true; 57 | } 58 | PopID(); 59 | win->DrawList->AddCircleFilled( 60 | pos, TIMELINE_RADIUS, IsItemActive() || IsItemHovered() ? active_color : inactive_color); 61 | } 62 | 63 | ImVec2 start = cursor_pos; 64 | start.x += win->Size.x * values[0] / s_max_timeline_value + 2 * TIMELINE_RADIUS; 65 | start.y += TIMELINE_RADIUS * 0.5f; 66 | ImVec2 end = start + ImVec2(win->Size.x * (values[1] - values[0]) / s_max_timeline_value - 2 * TIMELINE_RADIUS, 67 | TIMELINE_RADIUS); 68 | 69 | PushID(-1); 70 | SetCursorScreenPos(start); 71 | InvisibleButton(str_id, end - start); 72 | if (IsItemActive() && IsMouseDragging(0)) 73 | { 74 | values[0] += GetIO().MouseDelta.x / win->Size.x * s_max_timeline_value; 75 | values[1] += GetIO().MouseDelta.x / win->Size.x * s_max_timeline_value; 76 | changed = true; 77 | } 78 | PopID(); 79 | 80 | SetCursorScreenPos(cursor_pos + ImVec2(0, GetTextLineHeightWithSpacing())); 81 | 82 | win->DrawList->AddRectFilled(start, end, IsItemActive() || IsItemHovered() ? active_color : inactive_color); 83 | 84 | if (values[0] > values[1]) 85 | { 86 | float tmp = values[0]; 87 | values[0] = values[1]; 88 | values[1] = tmp; 89 | } 90 | if (values[1] > s_max_timeline_value) values[1] = s_max_timeline_value; 91 | if (values[0] < 0) values[0] = 0; 92 | return changed; 93 | } 94 | 95 | 96 | void EndTimeline(float t) 97 | { 98 | ImGuiWindow* win = GetCurrentWindow(); 99 | 100 | // @r-lyeh { 101 | if (t >= 0) { 102 | if (t > s_max_timeline_value) t = s_max_timeline_value; t /= s_max_timeline_value; 103 | const ImU32 line_color = ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_SeparatorActive]); 104 | ImVec2 a(win->Pos.x + GetWindowContentRegionMin().x + t * GetWindowContentRegionWidth(), GetWindowContentRegionMin().y + win->Pos.y + win->Scroll.y); 105 | ImVec2 b(win->Pos.x + GetWindowContentRegionMin().x + t * GetWindowContentRegionWidth(), GetWindowContentRegionMax().y + win->Pos.y + win->Scroll.y); 106 | win->DrawList->AddLine(a, b, line_color); 107 | } 108 | // } 109 | 110 | ImU32 color = ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_Button]); 111 | ImU32 line_color = ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_Border]); 112 | ImU32 text_color = ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_Text]); 113 | float rounding = GImGui->Style.ScrollbarRounding; 114 | ImVec2 start(GetWindowContentRegionMin().x + win->Pos.x, 115 | GetWindowContentRegionMax().y - GetTextLineHeightWithSpacing() + win->Pos.y + win->Scroll.y); 116 | ImVec2 end = GetWindowContentRegionMax() + win->Pos + ImVec2(0, win->Scroll.y); 117 | 118 | win->DrawList->AddRectFilled(start, end, color, rounding); 119 | 120 | const int LINE_COUNT = 5; 121 | const ImVec2 text_offset(0, GetTextLineHeightWithSpacing()); 122 | for (int i = 0; i <= LINE_COUNT; ++i) 123 | { 124 | ImVec2 a = GetWindowContentRegionMin() + win->Pos; // @r-lyeh: - ImVec2(TIMELINE_RADIUS, 0); 125 | a.x += i * (GetWindowContentRegionWidth() - 1) / LINE_COUNT; // @r-lyeh: -1 126 | ImVec2 b = a; 127 | b.y = start.y; 128 | win->DrawList->AddLine(a, b, line_color); 129 | char tmp[256]; 130 | ImFormatString(tmp, sizeof(tmp), "%.2f", i * s_max_timeline_value / LINE_COUNT); 131 | win->DrawList->AddText(b, text_color, tmp); 132 | } 133 | 134 | EndChild(); 135 | } 136 | 137 | } -------------------------------------------------------------------------------- /ControllerOverlay/imgui/imgui_timeline.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | namespace ImGui { 3 | 4 | bool BeginTimeline(const char* str_id, float max_time); 5 | bool TimelineEvent(const char* str_id, float times[2]); 6 | void EndTimeline(float current_time = -1); 7 | 8 | } 9 | 10 | -------------------------------------------------------------------------------- /ControllerOverlay/imgui/imstb_rectpack.h: -------------------------------------------------------------------------------- 1 | // [DEAR IMGUI] 2 | // This is a slightly modified version of stb_rect_pack.h 1.00. 3 | // Those changes would need to be pushed into nothings/stb: 4 | // - Added STBRP__CDECL 5 | // Grep for [DEAR IMGUI] to find the changes. 6 | 7 | // stb_rect_pack.h - v1.00 - public domain - rectangle packing 8 | // Sean Barrett 2014 9 | // 10 | // Useful for e.g. packing rectangular textures into an atlas. 11 | // Does not do rotation. 12 | // 13 | // Not necessarily the awesomest packing method, but better than 14 | // the totally naive one in stb_truetype (which is primarily what 15 | // this is meant to replace). 16 | // 17 | // Has only had a few tests run, may have issues. 18 | // 19 | // More docs to come. 20 | // 21 | // No memory allocations; uses qsort() and assert() from stdlib. 22 | // Can override those by defining STBRP_SORT and STBRP_ASSERT. 23 | // 24 | // This library currently uses the Skyline Bottom-Left algorithm. 25 | // 26 | // Please note: better rectangle packers are welcome! Please 27 | // implement them to the same API, but with a different init 28 | // function. 29 | // 30 | // Credits 31 | // 32 | // Library 33 | // Sean Barrett 34 | // Minor features 35 | // Martins Mozeiko 36 | // github:IntellectualKitty 37 | // 38 | // Bugfixes / warning fixes 39 | // Jeremy Jaussaud 40 | // Fabian Giesen 41 | // 42 | // Version history: 43 | // 44 | // 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles 45 | // 0.99 (2019-02-07) warning fixes 46 | // 0.11 (2017-03-03) return packing success/fail result 47 | // 0.10 (2016-10-25) remove cast-away-const to avoid warnings 48 | // 0.09 (2016-08-27) fix compiler warnings 49 | // 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) 50 | // 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) 51 | // 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort 52 | // 0.05: added STBRP_ASSERT to allow replacing assert 53 | // 0.04: fixed minor bug in STBRP_LARGE_RECTS support 54 | // 0.01: initial release 55 | // 56 | // LICENSE 57 | // 58 | // See end of file for license information. 59 | 60 | ////////////////////////////////////////////////////////////////////////////// 61 | // 62 | // INCLUDE SECTION 63 | // 64 | 65 | #ifndef STB_INCLUDE_STB_RECT_PACK_H 66 | #define STB_INCLUDE_STB_RECT_PACK_H 67 | 68 | #define STB_RECT_PACK_VERSION 1 69 | 70 | #ifdef STBRP_STATIC 71 | #define STBRP_DEF static 72 | #else 73 | #define STBRP_DEF extern 74 | #endif 75 | 76 | #ifdef __cplusplus 77 | extern "C" { 78 | #endif 79 | 80 | typedef struct stbrp_context stbrp_context; 81 | typedef struct stbrp_node stbrp_node; 82 | typedef struct stbrp_rect stbrp_rect; 83 | 84 | #ifdef STBRP_LARGE_RECTS 85 | typedef int stbrp_coord; 86 | #else 87 | typedef unsigned short stbrp_coord; 88 | #endif 89 | 90 | STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); 91 | // Assign packed locations to rectangles. The rectangles are of type 92 | // 'stbrp_rect' defined below, stored in the array 'rects', and there 93 | // are 'num_rects' many of them. 94 | // 95 | // Rectangles which are successfully packed have the 'was_packed' flag 96 | // set to a non-zero value and 'x' and 'y' store the minimum location 97 | // on each axis (i.e. bottom-left in cartesian coordinates, top-left 98 | // if you imagine y increasing downwards). Rectangles which do not fit 99 | // have the 'was_packed' flag set to 0. 100 | // 101 | // You should not try to access the 'rects' array from another thread 102 | // while this function is running, as the function temporarily reorders 103 | // the array while it executes. 104 | // 105 | // To pack into another rectangle, you need to call stbrp_init_target 106 | // again. To continue packing into the same rectangle, you can call 107 | // this function again. Calling this multiple times with multiple rect 108 | // arrays will probably produce worse packing results than calling it 109 | // a single time with the full rectangle array, but the option is 110 | // available. 111 | // 112 | // The function returns 1 if all of the rectangles were successfully 113 | // packed and 0 otherwise. 114 | 115 | struct stbrp_rect 116 | { 117 | // reserved for your use: 118 | int id; 119 | 120 | // input: 121 | stbrp_coord w, h; 122 | 123 | // output: 124 | stbrp_coord x, y; 125 | int was_packed; // non-zero if valid packing 126 | 127 | }; // 16 bytes, nominally 128 | 129 | 130 | STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); 131 | // Initialize a rectangle packer to: 132 | // pack a rectangle that is 'width' by 'height' in dimensions 133 | // using temporary storage provided by the array 'nodes', which is 'num_nodes' long 134 | // 135 | // You must call this function every time you start packing into a new target. 136 | // 137 | // There is no "shutdown" function. The 'nodes' memory must stay valid for 138 | // the following stbrp_pack_rects() call (or calls), but can be freed after 139 | // the call (or calls) finish. 140 | // 141 | // Note: to guarantee best results, either: 142 | // 1. make sure 'num_nodes' >= 'width' 143 | // or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' 144 | // 145 | // If you don't do either of the above things, widths will be quantized to multiples 146 | // of small integers to guarantee the algorithm doesn't run out of temporary storage. 147 | // 148 | // If you do #2, then the non-quantized algorithm will be used, but the algorithm 149 | // may run out of temporary storage and be unable to pack some rectangles. 150 | 151 | STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); 152 | // Optionally call this function after init but before doing any packing to 153 | // change the handling of the out-of-temp-memory scenario, described above. 154 | // If you call init again, this will be reset to the default (false). 155 | 156 | 157 | STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); 158 | // Optionally select which packing heuristic the library should use. Different 159 | // heuristics will produce better/worse results for different data sets. 160 | // If you call init again, this will be reset to the default. 161 | 162 | enum 163 | { 164 | STBRP_HEURISTIC_Skyline_default=0, 165 | STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, 166 | STBRP_HEURISTIC_Skyline_BF_sortHeight 167 | }; 168 | 169 | 170 | ////////////////////////////////////////////////////////////////////////////// 171 | // 172 | // the details of the following structures don't matter to you, but they must 173 | // be visible so you can handle the memory allocations for them 174 | 175 | struct stbrp_node 176 | { 177 | stbrp_coord x,y; 178 | stbrp_node *next; 179 | }; 180 | 181 | struct stbrp_context 182 | { 183 | int width; 184 | int height; 185 | int align; 186 | int init_mode; 187 | int heuristic; 188 | int num_nodes; 189 | stbrp_node *active_head; 190 | stbrp_node *free_head; 191 | stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' 192 | }; 193 | 194 | #ifdef __cplusplus 195 | } 196 | #endif 197 | 198 | #endif 199 | 200 | ////////////////////////////////////////////////////////////////////////////// 201 | // 202 | // IMPLEMENTATION SECTION 203 | // 204 | 205 | #ifdef STB_RECT_PACK_IMPLEMENTATION 206 | #ifndef STBRP_SORT 207 | #include 208 | #define STBRP_SORT qsort 209 | #endif 210 | 211 | #ifndef STBRP_ASSERT 212 | #include 213 | #define STBRP_ASSERT assert 214 | #endif 215 | 216 | // [DEAR IMGUI] Added STBRP__CDECL 217 | #ifdef _MSC_VER 218 | #define STBRP__NOTUSED(v) (void)(v) 219 | #define STBRP__CDECL __cdecl 220 | #else 221 | #define STBRP__NOTUSED(v) (void)sizeof(v) 222 | #define STBRP__CDECL 223 | #endif 224 | 225 | enum 226 | { 227 | STBRP__INIT_skyline = 1 228 | }; 229 | 230 | STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) 231 | { 232 | switch (context->init_mode) { 233 | case STBRP__INIT_skyline: 234 | STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); 235 | context->heuristic = heuristic; 236 | break; 237 | default: 238 | STBRP_ASSERT(0); 239 | } 240 | } 241 | 242 | STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) 243 | { 244 | if (allow_out_of_mem) 245 | // if it's ok to run out of memory, then don't bother aligning them; 246 | // this gives better packing, but may fail due to OOM (even though 247 | // the rectangles easily fit). @TODO a smarter approach would be to only 248 | // quantize once we've hit OOM, then we could get rid of this parameter. 249 | context->align = 1; 250 | else { 251 | // if it's not ok to run out of memory, then quantize the widths 252 | // so that num_nodes is always enough nodes. 253 | // 254 | // I.e. num_nodes * align >= width 255 | // align >= width / num_nodes 256 | // align = ceil(width/num_nodes) 257 | 258 | context->align = (context->width + context->num_nodes-1) / context->num_nodes; 259 | } 260 | } 261 | 262 | STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) 263 | { 264 | int i; 265 | #ifndef STBRP_LARGE_RECTS 266 | STBRP_ASSERT(width <= 0xffff && height <= 0xffff); 267 | #endif 268 | 269 | for (i=0; i < num_nodes-1; ++i) 270 | nodes[i].next = &nodes[i+1]; 271 | nodes[i].next = NULL; 272 | context->init_mode = STBRP__INIT_skyline; 273 | context->heuristic = STBRP_HEURISTIC_Skyline_default; 274 | context->free_head = &nodes[0]; 275 | context->active_head = &context->extra[0]; 276 | context->width = width; 277 | context->height = height; 278 | context->num_nodes = num_nodes; 279 | stbrp_setup_allow_out_of_mem(context, 0); 280 | 281 | // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) 282 | context->extra[0].x = 0; 283 | context->extra[0].y = 0; 284 | context->extra[0].next = &context->extra[1]; 285 | context->extra[1].x = (stbrp_coord) width; 286 | #ifdef STBRP_LARGE_RECTS 287 | context->extra[1].y = (1<<30); 288 | #else 289 | context->extra[1].y = 65535; 290 | #endif 291 | context->extra[1].next = NULL; 292 | } 293 | 294 | // find minimum y position if it starts at x1 295 | static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) 296 | { 297 | stbrp_node *node = first; 298 | int x1 = x0 + width; 299 | int min_y, visited_width, waste_area; 300 | 301 | STBRP__NOTUSED(c); 302 | 303 | STBRP_ASSERT(first->x <= x0); 304 | 305 | #if 0 306 | // skip in case we're past the node 307 | while (node->next->x <= x0) 308 | ++node; 309 | #else 310 | STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency 311 | #endif 312 | 313 | STBRP_ASSERT(node->x <= x0); 314 | 315 | min_y = 0; 316 | waste_area = 0; 317 | visited_width = 0; 318 | while (node->x < x1) { 319 | if (node->y > min_y) { 320 | // raise min_y higher. 321 | // we've accounted for all waste up to min_y, 322 | // but we'll now add more waste for everything we've visted 323 | waste_area += visited_width * (node->y - min_y); 324 | min_y = node->y; 325 | // the first time through, visited_width might be reduced 326 | if (node->x < x0) 327 | visited_width += node->next->x - x0; 328 | else 329 | visited_width += node->next->x - node->x; 330 | } else { 331 | // add waste area 332 | int under_width = node->next->x - node->x; 333 | if (under_width + visited_width > width) 334 | under_width = width - visited_width; 335 | waste_area += under_width * (min_y - node->y); 336 | visited_width += under_width; 337 | } 338 | node = node->next; 339 | } 340 | 341 | *pwaste = waste_area; 342 | return min_y; 343 | } 344 | 345 | typedef struct 346 | { 347 | int x,y; 348 | stbrp_node **prev_link; 349 | } stbrp__findresult; 350 | 351 | static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) 352 | { 353 | int best_waste = (1<<30), best_x, best_y = (1 << 30); 354 | stbrp__findresult fr; 355 | stbrp_node **prev, *node, *tail, **best = NULL; 356 | 357 | // align to multiple of c->align 358 | width = (width + c->align - 1); 359 | width -= width % c->align; 360 | STBRP_ASSERT(width % c->align == 0); 361 | 362 | // if it can't possibly fit, bail immediately 363 | if (width > c->width || height > c->height) { 364 | fr.prev_link = NULL; 365 | fr.x = fr.y = 0; 366 | return fr; 367 | } 368 | 369 | node = c->active_head; 370 | prev = &c->active_head; 371 | while (node->x + width <= c->width) { 372 | int y,waste; 373 | y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); 374 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL 375 | // bottom left 376 | if (y < best_y) { 377 | best_y = y; 378 | best = prev; 379 | } 380 | } else { 381 | // best-fit 382 | if (y + height <= c->height) { 383 | // can only use it if it first vertically 384 | if (y < best_y || (y == best_y && waste < best_waste)) { 385 | best_y = y; 386 | best_waste = waste; 387 | best = prev; 388 | } 389 | } 390 | } 391 | prev = &node->next; 392 | node = node->next; 393 | } 394 | 395 | best_x = (best == NULL) ? 0 : (*best)->x; 396 | 397 | // if doing best-fit (BF), we also have to try aligning right edge to each node position 398 | // 399 | // e.g, if fitting 400 | // 401 | // ____________________ 402 | // |____________________| 403 | // 404 | // into 405 | // 406 | // | | 407 | // | ____________| 408 | // |____________| 409 | // 410 | // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned 411 | // 412 | // This makes BF take about 2x the time 413 | 414 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { 415 | tail = c->active_head; 416 | node = c->active_head; 417 | prev = &c->active_head; 418 | // find first node that's admissible 419 | while (tail->x < width) 420 | tail = tail->next; 421 | while (tail) { 422 | int xpos = tail->x - width; 423 | int y,waste; 424 | STBRP_ASSERT(xpos >= 0); 425 | // find the left position that matches this 426 | while (node->next->x <= xpos) { 427 | prev = &node->next; 428 | node = node->next; 429 | } 430 | STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); 431 | y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); 432 | if (y + height <= c->height) { 433 | if (y <= best_y) { 434 | if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { 435 | best_x = xpos; 436 | STBRP_ASSERT(y <= best_y); 437 | best_y = y; 438 | best_waste = waste; 439 | best = prev; 440 | } 441 | } 442 | } 443 | tail = tail->next; 444 | } 445 | } 446 | 447 | fr.prev_link = best; 448 | fr.x = best_x; 449 | fr.y = best_y; 450 | return fr; 451 | } 452 | 453 | static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) 454 | { 455 | // find best position according to heuristic 456 | stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); 457 | stbrp_node *node, *cur; 458 | 459 | // bail if: 460 | // 1. it failed 461 | // 2. the best node doesn't fit (we don't always check this) 462 | // 3. we're out of memory 463 | if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { 464 | res.prev_link = NULL; 465 | return res; 466 | } 467 | 468 | // on success, create new node 469 | node = context->free_head; 470 | node->x = (stbrp_coord) res.x; 471 | node->y = (stbrp_coord) (res.y + height); 472 | 473 | context->free_head = node->next; 474 | 475 | // insert the new node into the right starting point, and 476 | // let 'cur' point to the remaining nodes needing to be 477 | // stiched back in 478 | 479 | cur = *res.prev_link; 480 | if (cur->x < res.x) { 481 | // preserve the existing one, so start testing with the next one 482 | stbrp_node *next = cur->next; 483 | cur->next = node; 484 | cur = next; 485 | } else { 486 | *res.prev_link = node; 487 | } 488 | 489 | // from here, traverse cur and free the nodes, until we get to one 490 | // that shouldn't be freed 491 | while (cur->next && cur->next->x <= res.x + width) { 492 | stbrp_node *next = cur->next; 493 | // move the current node to the free list 494 | cur->next = context->free_head; 495 | context->free_head = cur; 496 | cur = next; 497 | } 498 | 499 | // stitch the list back in 500 | node->next = cur; 501 | 502 | if (cur->x < res.x + width) 503 | cur->x = (stbrp_coord) (res.x + width); 504 | 505 | #ifdef _DEBUG 506 | cur = context->active_head; 507 | while (cur->x < context->width) { 508 | STBRP_ASSERT(cur->x < cur->next->x); 509 | cur = cur->next; 510 | } 511 | STBRP_ASSERT(cur->next == NULL); 512 | 513 | { 514 | int count=0; 515 | cur = context->active_head; 516 | while (cur) { 517 | cur = cur->next; 518 | ++count; 519 | } 520 | cur = context->free_head; 521 | while (cur) { 522 | cur = cur->next; 523 | ++count; 524 | } 525 | STBRP_ASSERT(count == context->num_nodes+2); 526 | } 527 | #endif 528 | 529 | return res; 530 | } 531 | 532 | // [DEAR IMGUI] Added STBRP__CDECL 533 | static int STBRP__CDECL rect_height_compare(const void *a, const void *b) 534 | { 535 | const stbrp_rect *p = (const stbrp_rect *) a; 536 | const stbrp_rect *q = (const stbrp_rect *) b; 537 | if (p->h > q->h) 538 | return -1; 539 | if (p->h < q->h) 540 | return 1; 541 | return (p->w > q->w) ? -1 : (p->w < q->w); 542 | } 543 | 544 | // [DEAR IMGUI] Added STBRP__CDECL 545 | static int STBRP__CDECL rect_original_order(const void *a, const void *b) 546 | { 547 | const stbrp_rect *p = (const stbrp_rect *) a; 548 | const stbrp_rect *q = (const stbrp_rect *) b; 549 | return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); 550 | } 551 | 552 | #ifdef STBRP_LARGE_RECTS 553 | #define STBRP__MAXVAL 0xffffffff 554 | #else 555 | #define STBRP__MAXVAL 0xffff 556 | #endif 557 | 558 | STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) 559 | { 560 | int i, all_rects_packed = 1; 561 | 562 | // we use the 'was_packed' field internally to allow sorting/unsorting 563 | for (i=0; i < num_rects; ++i) { 564 | rects[i].was_packed = i; 565 | } 566 | 567 | // sort according to heuristic 568 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); 569 | 570 | for (i=0; i < num_rects; ++i) { 571 | if (rects[i].w == 0 || rects[i].h == 0) { 572 | rects[i].x = rects[i].y = 0; // empty rect needs no space 573 | } else { 574 | stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); 575 | if (fr.prev_link) { 576 | rects[i].x = (stbrp_coord) fr.x; 577 | rects[i].y = (stbrp_coord) fr.y; 578 | } else { 579 | rects[i].x = rects[i].y = STBRP__MAXVAL; 580 | } 581 | } 582 | } 583 | 584 | // unsort 585 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); 586 | 587 | // set was_packed flags and all_rects_packed status 588 | for (i=0; i < num_rects; ++i) { 589 | rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); 590 | if (!rects[i].was_packed) 591 | all_rects_packed = 0; 592 | } 593 | 594 | // return the all_rects_packed status 595 | return all_rects_packed; 596 | } 597 | #endif 598 | 599 | /* 600 | ------------------------------------------------------------------------------ 601 | This software is available under 2 licenses -- choose whichever you prefer. 602 | ------------------------------------------------------------------------------ 603 | ALTERNATIVE A - MIT License 604 | Copyright (c) 2017 Sean Barrett 605 | Permission is hereby granted, free of charge, to any person obtaining a copy of 606 | this software and associated documentation files (the "Software"), to deal in 607 | the Software without restriction, including without limitation the rights to 608 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 609 | of the Software, and to permit persons to whom the Software is furnished to do 610 | so, subject to the following conditions: 611 | The above copyright notice and this permission notice shall be included in all 612 | copies or substantial portions of the Software. 613 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 614 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 615 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 616 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 617 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 618 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 619 | SOFTWARE. 620 | ------------------------------------------------------------------------------ 621 | ALTERNATIVE B - Public Domain (www.unlicense.org) 622 | This is free and unencumbered software released into the public domain. 623 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 624 | software, either in source code form or as a compiled binary, for any purpose, 625 | commercial or non-commercial, and by any means. 626 | In jurisdictions that recognize copyright laws, the author or authors of this 627 | software dedicate any and all copyright interest in the software to the public 628 | domain. We make this dedication for the benefit of the public at large and to 629 | the detriment of our heirs and successors. We intend this dedication to be an 630 | overt act of relinquishment in perpetuity of all present and future rights to 631 | this software under copyright law. 632 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 633 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 634 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 635 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 636 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 637 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 638 | ------------------------------------------------------------------------------ 639 | */ 640 | --------------------------------------------------------------------------------