├── .github └── workflows │ └── build.yml ├── .gitignore ├── LICENSE ├── README.md ├── SimpleWindowSwitcher.sln └── SimpleWindowSwitcher ├── SimpleWindowSwitcher.vcxproj ├── SimpleWindowSwitcher.vcxproj.filters ├── main.c ├── sws_IconPainter.c ├── sws_IconPainter.h ├── sws_RegistryMonitor.c ├── sws_RegistryMonitor.h ├── sws_WindowHelpers.c ├── sws_WindowHelpers.h ├── sws_WindowSwitcher.c ├── sws_WindowSwitcher.h ├── sws_WindowSwitcherLayout.c ├── sws_WindowSwitcherLayout.h ├── sws_WindowSwitcherLayoutWindow.c ├── sws_WindowSwitcherLayoutWindow.h ├── sws_def.h ├── sws_dll.h ├── sws_error.c ├── sws_error.h ├── sws_tshwnd.c ├── sws_tshwnd.h ├── sws_utility.h ├── sws_vector.c ├── sws_vector.h ├── sws_window.c └── sws_window.h /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: windows-latest 8 | 9 | steps: 10 | - name: Checkout latest build 11 | uses: actions/checkout@v2 12 | 13 | - name: Declare some variables 14 | id: vars 15 | shell: bash 16 | run: | 17 | echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})" 18 | echo "::set-output name=sha_short::$(git rev-parse --short HEAD)" 19 | 20 | - name: Add MSBuild to PATH 21 | uses: microsoft/setup-msbuild@v1.0.2 22 | 23 | - name: Build 24 | working-directory: ${{env.GITHUB_WORKSPACE}} 25 | # Add additional options to the MSBuild command line here (like platform or verbosity level). 26 | # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference 27 | run: msbuild /m /p:Configuration=Release /p:Platform=x64 SimpleWindowSwitcher.sln 28 | 29 | - name: Upload artifacts 30 | uses: actions/upload-artifact@v2 31 | with: 32 | name: sws_bin_amd64_${{ steps.vars.outputs.sha_short }}_${{ steps.vars.outputs.branch }} 33 | path: | 34 | x64/Release/ 35 | -------------------------------------------------------------------------------- /.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 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple Window Switcher 2 | Simple Window Switcher (sws) is an Alt-Tab replacement for Windows. 3 | 4 | ![Build status](https://github.com/valinet/sws/actions/workflows/build.yml/badge.svg) 5 | 6 | ## Example usage 7 | An application which implements this is [ExplorerPatcher](https://github.com/valinet/ExplorerPatcher). This project can also be compiled as a library and used standalone in your current workflow. 8 | 9 | ## Compiling 10 | 11 | If you need more information, you can also consult the GitHub Actions automated build file [here](https://github.com/valinet/sws/actions/workflows/msbuild.yml). 12 | 13 | The following prerequisites are necessary in order to compile this project: 14 | 15 | * Microsoft C/C++ Optimizing Compiler - this can be obtained by installing either of these packages: 16 | 17 | * Visual Studio - this is a fully featured IDE; you'll need to check "C/C++ application development role" when installing. If you do not require the full suite, use the package bellow. 18 | * Build Tools for Visual Studio - this just installs the compiler, which you'll be able to use from the command line, or from other applications like CMake 19 | 20 | Download either of those [here](http://go.microsoft.com/fwlink/p/?LinkId=840931). The guide assumes you have installed either Visual Studio 2019, either Build Tools for Visual Studio 2019. 21 | 22 | * A recent version of the [Windows SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk/) - for development, version 10.0.19041.0 was used, available [here](https://go.microsoft.com/fwlink/p/?linkid=2120843) (this may also be offered as an option when installing Visual Studio) 23 | 24 | * Git - you can use [Git for Windows](https://git-scm.com/download/win), or git command via the Windows Subsystem for Linux. 25 | 26 | Steps: 27 | 28 | 1. Clone git repo 29 | 30 | ``` 31 | git clone https://github.com/valinet/sws 32 | ``` 33 | 34 | If "git" is not found as a command, type its full path, or have its folder added to PATH, or open Git command window in the respective folder if using Git for Windows. 35 | 36 | 2. Compile sws 37 | 38 | * Double click the `SimpleWindowSwitcher.sln` file to open the solution in Visual Studio. Choose Release and your processor architecture in the toolbar. Press `[Ctrl]`+`[Shift]`+`[B]` or choose "Build" - "Build solution" to compile. 39 | 40 | * Open an "x86 Native Tools Command Prompt for VS 2019" (for x86), or "x64 Native Tools Command Prompt for VS 2019" (for x64) (search that in Start), go to folder containing solution file and type: 41 | 42 | * For x86: 43 | 44 | ``` 45 | msbuild ExplorerPatcher.sln /property:Configuration=Release /property:Platform=x86 46 | ``` 47 | 48 | * For x64: 49 | 50 | ``` 51 | msbuild ExplorerPatcher.sln /property:Configuration=Release /property:Platform=x64 52 | ``` 53 | 54 | The resulting libraries will be in the "Release" (for x86) or "x64\Release" (for x64) folder in the directory containing the solution file. 55 | 56 | That's it. 57 | -------------------------------------------------------------------------------- /SimpleWindowSwitcher.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31624.102 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SimpleWindowSwitcher", "SimpleWindowSwitcher\SimpleWindowSwitcher.vcxproj", "{FCDC9E89-976E-40D0-A1D0-2BD773720742}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Debug|arm64 = Debug|arm64 13 | Release|x64 = Release|x64 14 | Release|x86 = Release|x86 15 | Release|arm64 = Release|arm64 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {FCDC9E89-976E-40D0-A1D0-2BD773720742}.Debug|x64.ActiveCfg = Debug|x64 19 | {FCDC9E89-976E-40D0-A1D0-2BD773720742}.Debug|x64.Build.0 = Debug|x64 20 | {FCDC9E89-976E-40D0-A1D0-2BD773720742}.Debug|x86.ActiveCfg = Debug|Win32 21 | {FCDC9E89-976E-40D0-A1D0-2BD773720742}.Debug|x86.Build.0 = Debug|Win32 22 | {FCDC9E89-976E-40D0-A1D0-2BD773720742}.Debug|arm64.ActiveCfg = Debug|ARM64 23 | {FCDC9E89-976E-40D0-A1D0-2BD773720742}.Debug|arm64.Build.0 = Debug|ARM64 24 | {FCDC9E89-976E-40D0-A1D0-2BD773720742}.Release|x64.ActiveCfg = Release|x64 25 | {FCDC9E89-976E-40D0-A1D0-2BD773720742}.Release|x64.Build.0 = Release|x64 26 | {FCDC9E89-976E-40D0-A1D0-2BD773720742}.Release|x86.ActiveCfg = Release|Win32 27 | {FCDC9E89-976E-40D0-A1D0-2BD773720742}.Release|x86.Build.0 = Release|Win32 28 | {FCDC9E89-976E-40D0-A1D0-2BD773720742}.Release|arm64.ActiveCfg = Release|ARM64 29 | {FCDC9E89-976E-40D0-A1D0-2BD773720742}.Release|arm64.Build.0 = Release|ARM64 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {8652CF1F-1365-418F-9FD1-9ED86EA9E012} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /SimpleWindowSwitcher/SimpleWindowSwitcher.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 | Debug 22 | ARM64 23 | 24 | 25 | Release 26 | ARM64 27 | 28 | 29 | 30 | 16.0 31 | Win32Proj 32 | {fcdc9e89-976e-40d0-a1d0-2bd773720742} 33 | SimpleWindowSwitcher 34 | 10.0 35 | 36 | 37 | 38 | Application 39 | true 40 | v142 41 | Unicode 42 | 43 | 44 | Application 45 | false 46 | v142 47 | true 48 | Unicode 49 | 50 | 51 | DynamicLibrary 52 | true 53 | v142 54 | Unicode 55 | 56 | 57 | DynamicLibrary 58 | false 59 | v142 60 | true 61 | Unicode 62 | 63 | 64 | DynamicLibrary 65 | true 66 | v142 67 | Unicode 68 | 69 | 70 | DynamicLibrary 71 | false 72 | v142 73 | true 74 | Unicode 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | true 102 | 103 | 104 | false 105 | 106 | 107 | true 108 | 109 | 110 | false 111 | 112 | 113 | 114 | Level3 115 | true 116 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 117 | true 118 | 119 | 120 | Console 121 | true 122 | 123 | 124 | 125 | 126 | Level3 127 | true 128 | true 129 | true 130 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 131 | true 132 | 133 | 134 | Console 135 | true 136 | true 137 | true 138 | 139 | 140 | 141 | 142 | Level3 143 | true 144 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 145 | true 146 | stdcpp17 147 | MultiThreadedDebug 148 | 149 | 150 | Console 151 | true 152 | RequireAdministrator 153 | 154 | 155 | 156 | 157 | Level3 158 | true 159 | true 160 | true 161 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 162 | true 163 | stdcpp17 164 | MultiThreaded 165 | 166 | 167 | Console 168 | true 169 | true 170 | true 171 | RequireAdministrator 172 | 173 | 174 | 175 | 176 | Level3 177 | true 178 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 179 | true 180 | stdcpp17 181 | MultiThreadedDebug 182 | 183 | 184 | Console 185 | true 186 | RequireAdministrator 187 | 188 | 189 | 190 | 191 | Level3 192 | true 193 | true 194 | true 195 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 196 | true 197 | stdcpp17 198 | MultiThreaded 199 | 200 | 201 | Console 202 | true 203 | true 204 | true 205 | RequireAdministrator 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | -------------------------------------------------------------------------------- /SimpleWindowSwitcher/SimpleWindowSwitcher.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;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 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | Source Files 44 | 45 | 46 | Source Files 47 | 48 | 49 | Source Files 50 | 51 | 52 | 53 | 54 | Header Files 55 | 56 | 57 | Header Files 58 | 59 | 60 | Header Files 61 | 62 | 63 | Header Files 64 | 65 | 66 | Header Files 67 | 68 | 69 | Header Files 70 | 71 | 72 | Header Files 73 | 74 | 75 | Header Files 76 | 77 | 78 | Header Files 79 | 80 | 81 | Header Files 82 | 83 | 84 | Header Files 85 | 86 | 87 | Header Files 88 | 89 | 90 | Header Files 91 | 92 | 93 | -------------------------------------------------------------------------------- /SimpleWindowSwitcher/main.c: -------------------------------------------------------------------------------- 1 | #define UNICODE 2 | #define _CRTDBG_MAP_ALLOC 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "sws_def.h" 9 | #include "sws_dll.h" 10 | #include "sws_error.h" 11 | #include "sws_WindowSwitcher.h" 12 | 13 | BOOL g_bIsDesktopRaised = FALSE; 14 | 15 | __declspec(dllexport) sws_error_t main(DWORD unused) 16 | { 17 | sws_error_t rv = SWS_ERROR_SUCCESS; 18 | void* sws = NULL; 19 | 20 | if (!rv) 21 | { 22 | rv = sws_WindowSwitcher_Initialize(&sws, TRUE); 23 | } 24 | if (!rv) 25 | { 26 | rv = sws_WindowSwitcher_RunMessageQueue(sws); 27 | } 28 | if (!rv) 29 | { 30 | sws_WindowSwitcher_Clear(sws); 31 | } 32 | return rv; 33 | } 34 | 35 | BOOL WINAPI DllMain( 36 | _In_ HINSTANCE hinstDLL, 37 | _In_ DWORD fdwReason, 38 | _In_ LPVOID lpvReserved 39 | ) 40 | { 41 | switch (fdwReason) 42 | { 43 | case DLL_PROCESS_ATTACH: 44 | DisableThreadLibraryCalls(hinstDLL); 45 | wchar_t exeName[MAX_PATH]; 46 | GetProcessImageFileNameW( 47 | OpenProcess( 48 | PROCESS_QUERY_INFORMATION, 49 | FALSE, 50 | GetCurrentProcessId() 51 | ), 52 | exeName, 53 | MAX_PATH 54 | ); 55 | PathStripPathW(exeName); 56 | wchar_t dllName[MAX_PATH]; 57 | GetModuleFileNameW(hinstDLL, dllName, MAX_PATH); 58 | PathStripPathW(dllName); 59 | if (!_wcsicmp(dllName, L"dxgi.dll")) 60 | { 61 | wchar_t wszSystemPath[MAX_PATH]; 62 | GetSystemDirectoryW(wszSystemPath, MAX_PATH); 63 | wcscat_s(wszSystemPath, MAX_PATH, L"\\dxgi.dll"); 64 | HMODULE hModule = LoadLibraryW(wszSystemPath); 65 | #pragma warning(disable : 6387) 66 | ApplyCompatResolutionQuirkingFunc = GetProcAddress(hModule, "ApplyCompatResolutionQuirking"); 67 | CompatStringFunc = GetProcAddress(hModule, "CompatString"); 68 | CompatValueFunc = GetProcAddress(hModule, "CompatValue"); 69 | CreateDXGIFactoryFunc = GetProcAddress(hModule, "CreateDXGIFactory"); 70 | CreateDXGIFactory1Func = GetProcAddress(hModule, "CreateDXGIFactory1"); 71 | CreateDXGIFactory2Func = GetProcAddress(hModule, "CreateDXGIFactory2"); 72 | DXGID3D10CreateDeviceFunc = GetProcAddress(hModule, "DXGID3D10CreateDevice"); 73 | DXGID3D10CreateLayeredDeviceFunc = GetProcAddress(hModule, "DXGID3D10CreateLayeredDevice"); 74 | DXGID3D10GetLayeredDeviceSizeFunc = GetProcAddress(hModule, "DXGID3D10GetLayeredDeviceSize"); 75 | DXGID3D10RegisterLayersFunc = GetProcAddress(hModule, "DXGID3D10RegisterLayers"); 76 | DXGIDeclareAdapterRemovalSupportFunc = GetProcAddress(hModule, "DXGIDeclareAdapterRemovalSupport"); 77 | DXGIDumpJournalFunc = GetProcAddress(hModule, "DXGIDumpJournal"); 78 | DXGIGetDebugInterface1Func = GetProcAddress(hModule, "DXGIGetDebugInterface1"); 79 | DXGIReportAdapterConfigurationFunc = GetProcAddress(hModule, "DXGIReportAdapterConfiguration"); 80 | PIXBeginCaptureFunc = GetProcAddress(hModule, "PIXBeginCapture"); 81 | PIXEndCaptureFunc = GetProcAddress(hModule, "PIXEndCapture"); 82 | PIXGetCaptureStateFunc = GetProcAddress(hModule, "PIXGetCaptureState"); 83 | SetAppCompatStringPointerFunc = GetProcAddress(hModule, "SetAppCompatStringPointer"); 84 | UpdateHMDEmulationStatusFunc = GetProcAddress(hModule, "UpdateHMDEmulationStatus"); 85 | #pragma warning(default : 6387) 86 | } 87 | break; 88 | case DLL_THREAD_ATTACH: 89 | break; 90 | case DLL_THREAD_DETACH: 91 | break; 92 | case DLL_PROCESS_DETACH: 93 | break; 94 | } 95 | return TRUE; 96 | } -------------------------------------------------------------------------------- /SimpleWindowSwitcher/sws_IconPainter.c: -------------------------------------------------------------------------------- 1 | #include "sws_IconPainter.h" 2 | #include "sws_WindowSwitcher.h" 3 | 4 | void sws_IconPainter_DrawIcon(HICON hIcon, HDC hDC, HBRUSH hBrush, void* pGdipGraphics, INT x, INT y, INT w, INT h, RGBQUAD bkcol, BOOL bShouldFillBackground) 5 | { 6 | if (hIcon == NULL || hDC == NULL || w == 0 || h == 0) 7 | { 8 | return; 9 | } 10 | 11 | BITMAPINFO bi; 12 | ZeroMemory(&bi, sizeof(BITMAPINFO)); 13 | bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 14 | bi.bmiHeader.biWidth = 1; 15 | bi.bmiHeader.biHeight = 1; 16 | bi.bmiHeader.biPlanes = 1; 17 | bi.bmiHeader.biBitCount = 32; 18 | bi.bmiHeader.biCompression = BI_RGB; 19 | if (bShouldFillBackground) 20 | { 21 | StretchDIBits(hDC, x, y, w, h, 0, 0, 1, 1, &bkcol, &bi, DIB_RGB_COLORS, SRCCOPY); 22 | } 23 | 24 | // Not using GdipCreateBitmapFromHICON directly because some bug in GDI+ 25 | // renders weird black lines in some transparent areas; this is the only 26 | // way I could properly get this to work 27 | // from: https://stackoverflow.com/questions/11338009/how-do-i-copy-an-hicon-from-gdi-to-gdi-with-transparency 28 | if (pGdipGraphics) 29 | { 30 | ICONINFO ii; 31 | if (GetIconInfo(hIcon, &ii)) 32 | { 33 | void* pGdipBitmap = NULL; 34 | GdipCreateBitmapFromHBITMAP( 35 | (HBITMAP)ii.hbmColor, 36 | (HPALETTE)NULL, 37 | (void*)&pGdipBitmap 38 | ); 39 | if (pGdipBitmap) 40 | { 41 | INT rct[4] = { 0, 0, 0, 0 }; 42 | GdipGetImageWidth((void*)pGdipBitmap, (UINT*)(rct + 2)); 43 | GdipGetImageHeight((void*)pGdipBitmap, (UINT*)(rct + 3)); 44 | INT PixelFormat; 45 | GdipGetImagePixelFormat((void*)pGdipBitmap, &PixelFormat); 46 | INT LockedBitmapData[100]; // make sure this is large enough 47 | GdipBitmapLockBits( 48 | (void*)pGdipBitmap, 49 | (INT*)rct, 50 | (INT)0, // Gdiplus::ImageLockModeRead 51 | (INT)PixelFormat, 52 | (void*)LockedBitmapData 53 | ); 54 | if (*(BYTE**)(LockedBitmapData + 4)) // Scan0 55 | { 56 | void* pGdipBitmap2 = NULL; 57 | GdipCreateBitmapFromScan0( 58 | (INT)LockedBitmapData[0], // Width 59 | (INT)LockedBitmapData[1], // Height 60 | (INT)LockedBitmapData[2], // Stride 61 | (INT)((10 | (32 << 8) | 0x00040000 | 0x00020000 | 0x00200000)), // PixelFormat = PixelFormat32bppARGB 62 | *(BYTE**)(LockedBitmapData + 4), // Scan0 63 | (void*)&pGdipBitmap2 64 | ); 65 | if (pGdipBitmap2) 66 | { 67 | GdipDrawImageRectI( 68 | (void*)pGdipGraphics, 69 | (void*)pGdipBitmap2, 70 | (INT)x, 71 | (INT)y, 72 | (INT)w, 73 | (INT)h 74 | ); 75 | GdipDisposeImage((void*)pGdipBitmap2); 76 | // We proceed to check if this worked by verifying if the 77 | // written bitmap is all transparent; if it is, then it 78 | // means nothing was drawn, so we fallback to creating the 79 | // bitmap using GdipCreateBitmapFromHICON and drawing that 80 | HDC hDC_Test = NULL; 81 | HBITMAP hBM_Test = NULL; 82 | HBITMAP hBM_Test_Old = NULL; 83 | if (hDC_Test = CreateCompatibleDC(hDC)) 84 | { 85 | if (hBM_Test = CreateCompatibleBitmap(hDC, w, h)) 86 | { 87 | if (hBM_Test_Old = SelectObject(hDC_Test, hBM_Test)) 88 | { 89 | if (BitBlt(hDC_Test, 0, 0, w, h, hDC, x, y, SRCCOPY)) 90 | { 91 | BITMAPINFO info; 92 | info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 93 | info.bmiHeader.biWidth = w; 94 | info.bmiHeader.biHeight = -h; 95 | info.bmiHeader.biPlanes = 1; 96 | info.bmiHeader.biBitCount = 32; 97 | info.bmiHeader.biCompression = BI_RGB; 98 | info.bmiHeader.biSizeImage = w * h * 4; 99 | info.bmiHeader.biXPelsPerMeter = 0; 100 | info.bmiHeader.biYPelsPerMeter = 0; 101 | info.bmiHeader.biClrUsed = 0; 102 | info.bmiHeader.biClrImportant = 0; 103 | void* bits = malloc(info.bmiHeader.biSizeImage); 104 | if (bits) 105 | { 106 | if (GetDIBits(hDC_Test, hBM_Test, 0, h, bits, &info, DIB_RGB_COLORS)) 107 | { 108 | BYTE* ptr; 109 | int ii; 110 | BOOL bUsed = FALSE; 111 | for (ii = 0, ptr = bits; ii < w * h; ii++, ptr += 4) 112 | { 113 | if (ptr[3] != bkcol.rgbReserved) 114 | { 115 | bUsed = TRUE; 116 | break; 117 | } 118 | } 119 | if (!bUsed) 120 | { 121 | if (bShouldFillBackground) 122 | { 123 | StretchDIBits(hDC, x, y, w, h, 0, 0, 1, 1, &bkcol, &bi, DIB_RGB_COLORS, SRCCOPY); 124 | } 125 | void* pGdipBitmap3 = NULL; 126 | GdipCreateBitmapFromHICON( 127 | (HICON)hIcon, 128 | (void**)&pGdipBitmap3 129 | ); 130 | if (pGdipBitmap3) 131 | { 132 | GdipDrawImageRectI( 133 | (void*)pGdipGraphics, 134 | (void*)pGdipBitmap3, 135 | (INT)x, 136 | (INT)y, 137 | (INT)w, 138 | (INT)h 139 | ); 140 | GdipDisposeImage((void*)pGdipBitmap3); 141 | } 142 | } 143 | } 144 | free(bits); 145 | } 146 | } 147 | SelectObject(hDC_Test, hBM_Test_Old); 148 | } 149 | DeleteObject(hBM_Test); 150 | } 151 | DeleteDC(hDC_Test); 152 | } 153 | } 154 | GdipBitmapUnlockBits( 155 | (void*)pGdipBitmap, 156 | (void*)&LockedBitmapData 157 | ); 158 | } 159 | GdipDisposeImage((void*)pGdipBitmap); 160 | } 161 | DeleteObject(ii.hbmColor); 162 | DeleteObject(ii.hbmMask); 163 | } 164 | } 165 | else 166 | { 167 | // Fallback to crappier drawing using GDI if GDI+ is unavailable 168 | if (bShouldFillBackground) 169 | { 170 | DrawIconEx(hDC, x, y, hIcon, w, h, 0, hBrush, DI_NORMAL); 171 | } 172 | else 173 | { 174 | DrawIcon(hDC, x, y, hIcon); 175 | } 176 | } 177 | } 178 | 179 | static void _sws_IconPainter_Callback( 180 | HWND hWnd, 181 | UINT uMsg, 182 | sws_IconPainter_CallbackParams* params, 183 | HICON hIcon 184 | ) 185 | { 186 | LONG_PTR ptr = GetWindowLongPtr(params->hWnd, GWLP_USERDATA); 187 | sws_WindowSwitcher* _this = (struct sws_WindowSwitcher*)(ptr); 188 | 189 | if (_this->layout.timestamp == params->timestamp) 190 | { 191 | DWORD dwProcessId; 192 | GetWindowThreadProcessId(hWnd, &dwProcessId); 193 | sws_WindowSwitcherLayoutWindow* pWindowList = _this->layout.pWindowList.pList; 194 | 195 | if (!hIcon || (params->bUseApplicationIcon && !pWindowList[params->index].dwIconSource)) 196 | { 197 | if (params->bUseApplicationIcon && !pWindowList[params->index].dwIconSource) 198 | { 199 | if (hIcon && dwProcessId != GetCurrentProcessId()) 200 | { 201 | DestroyIcon(hIcon); 202 | } 203 | pWindowList[params->index].dwIconSource = 1; 204 | } 205 | switch (pWindowList[params->index].dwIconSource) 206 | { 207 | case 0: 208 | { 209 | pWindowList[params->index].dwIconSource++; 210 | if (SendMessageCallbackW(hWnd, WM_GETICON, ICON_SMALL2, 0, _sws_IconPainter_Callback, params)) 211 | { 212 | return; 213 | } 214 | else 215 | { 216 | pWindowList[params->index].hIcon = sws_LegacyDefAppIcon; 217 | } 218 | break; 219 | } 220 | case 1: 221 | { 222 | pWindowList[params->index].dwIconSource++; 223 | wchar_t wszPath[MAX_PATH]; 224 | ZeroMemory(wszPath, MAX_PATH * sizeof(wchar_t)); 225 | if (!params->bIsDesktop) 226 | { 227 | if (!sws_WindowHelpers_IsWindowUWP(hWnd)) 228 | { 229 | HANDLE hProcess; 230 | hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, dwProcessId); 231 | if (hProcess) 232 | { 233 | GetModuleFileNameExW((HMODULE)hProcess, NULL, wszPath, MAX_PATH); 234 | CharLowerW(wszPath); 235 | SHFILEINFOW shinfo; 236 | ZeroMemory(&shinfo, sizeof(SHFILEINFOW)); 237 | SHGetFileInfoW( 238 | wszPath, 239 | FILE_ATTRIBUTE_NORMAL, 240 | &shinfo, 241 | sizeof(SHFILEINFOW), 242 | SHGFI_ICON 243 | ); 244 | if (shinfo.hIcon) 245 | { 246 | _sws_IconPainter_Callback(hWnd, uMsg, params, shinfo.hIcon); 247 | return; 248 | } 249 | } 250 | } 251 | else 252 | { 253 | HRESULT hr = S_OK; 254 | IShellItemImageFactory* imageFactory = NULL; 255 | SIIGBF flags = SIIGBF_RESIZETOFIT | SIIGBF_ICONBACKGROUND; 256 | 257 | IPropertyStore* propStore = NULL; 258 | hr = SHGetPropertyStoreForWindow( 259 | hWnd, 260 | &__uuidof_IPropertyStore, 261 | &propStore 262 | ); 263 | if (SUCCEEDED(hr)) 264 | { 265 | PROPERTYKEY pKey; 266 | pKey.fmtid = __uuidof_AppUserModelIdProperty; 267 | pKey.pid = 5; 268 | PROPVARIANT prop; 269 | ZeroMemory(&prop, sizeof(PROPVARIANT)); 270 | propStore->lpVtbl->GetValue(propStore, &pKey, &prop); 271 | propStore->lpVtbl->Release(propStore); 272 | if (prop.bstrVal) 273 | { 274 | SHCreateItemInKnownFolder( 275 | &FOLDERID_AppsFolder, 276 | KF_FLAG_DONT_VERIFY, 277 | prop.bstrVal, 278 | &__uuidof_IShellItemImageFactory, 279 | &imageFactory 280 | ); 281 | if (imageFactory) 282 | { 283 | double factor = SWS_UWP_ICON_SCALE_FACTOR; 284 | int szIcon = pWindowList[params->index].szIcon / factor; 285 | 286 | SIZE size; 287 | size.cx = szIcon; 288 | size.cy = szIcon; 289 | HBITMAP hBitmap; 290 | hr = imageFactory->lpVtbl->GetImage( 291 | imageFactory, 292 | size, 293 | flags, 294 | &hBitmap 295 | ); 296 | if (SUCCEEDED(hr)) 297 | { 298 | // Easiest way to get an HICON from an HBITMAP 299 | // I have turned the Internet upside down and was unable to find this 300 | // Only a convoluted example using GDI+ 301 | // This is from the disassembly of StartIsBack/StartAllBack 302 | HIMAGELIST hImageList = ImageList_Create(size.cx, size.cy, ILC_COLOR32, 1, 0); 303 | if (ImageList_Add(hImageList, hBitmap, NULL) != -1) 304 | { 305 | HICON hExIcon = ImageList_GetIcon(hImageList, 0, 0); 306 | ImageList_Destroy(hImageList); 307 | 308 | DeleteObject(hBitmap); 309 | imageFactory->lpVtbl->Release(imageFactory); 310 | 311 | pWindowList[params->index].dwWindowFlags |= SWS_WINDOWSWITCHERLAYOUT_WINDOWFLAGS_ISUWP; 312 | szIcon = pWindowList[params->index].rcIcon.right; 313 | szIcon = szIcon * factor; 314 | szIcon = pWindowList[params->index].rcIcon.right - szIcon; 315 | pWindowList[params->index].rcIcon.left = szIcon / 2; 316 | pWindowList[params->index].rcIcon.top = szIcon / 2; 317 | pWindowList[params->index].rcIcon.right = pWindowList[params->index].rcIcon.right + szIcon; 318 | pWindowList[params->index].rcIcon.bottom = pWindowList[params->index].rcIcon.bottom + szIcon; 319 | 320 | _sws_IconPainter_Callback(hWnd, uMsg, params, hExIcon); 321 | return; 322 | } 323 | DeleteObject(hBitmap); 324 | } 325 | imageFactory->lpVtbl->Release(imageFactory); 326 | } 327 | } 328 | } 329 | } 330 | } 331 | else 332 | { 333 | if (GetSystemDirectoryW(wszPath, MAX_PATH)) 334 | { 335 | wcscat_s(wszPath, MAX_PATH, L"\\imageres.dll"); 336 | HICON hExIcon = ExtractIconW( 337 | GetModuleHandleW(NULL), 338 | wszPath, 339 | -110 340 | ); 341 | if (hExIcon) 342 | { 343 | _sws_IconPainter_Callback(hWnd, uMsg, params, hExIcon); 344 | return; 345 | } 346 | } 347 | } 348 | pWindowList[params->index].hIcon = sws_LegacyDefAppIcon; 349 | } 350 | } 351 | } 352 | else 353 | { 354 | if (pWindowList[params->index].hIcon && sws_DefAppIcon && pWindowList[params->index].hIcon != sws_DefAppIcon && sws_LegacyDefAppIcon && pWindowList[params->index].hIcon != sws_LegacyDefAppIcon) 355 | { 356 | DestroyIcon(pWindowList[params->index].hIcon); 357 | pWindowList[params->index].hIcon = NULL; 358 | } 359 | if (dwProcessId == GetCurrentProcessId() && pWindowList[params->index].dwIconSource <= 1) 360 | { 361 | pWindowList[params->index].hIcon = CopyIcon(hIcon); 362 | } 363 | else 364 | { 365 | pWindowList[params->index].hIcon = hIcon; 366 | } 367 | } 368 | 369 | BOOL bShouldPaint = TRUE; 370 | for (unsigned int i = 0; i < _this->layout.pWindowList.cbSize; ++i) 371 | { 372 | if (pWindowList[i].hIcon == sws_DefAppIcon && !IsHungAppWindow(pWindowList[i].hWnd)) 373 | { 374 | bShouldPaint = FALSE; 375 | break; 376 | } 377 | } 378 | if (bShouldPaint) 379 | { 380 | //printf("[sws] Asynchronously obtained application icons in %lld ms.\n", sws_milliseconds_now() - _this->layout.timestamp); 381 | KillTimer(_this->hWnd, SWS_WINDOWSWITCHER_TIMER_PAINT); 382 | SendMessageW(_this->hWnd, SWS_WINDOWSWITCHER_PAINT_MSG, SWS_WINDOWSWITCHER_PAINTFLAGS_REDRAWENTIRE, 0); 383 | } 384 | } 385 | 386 | free(params); 387 | } 388 | 389 | BOOL sws_IconPainter_ExtractAndDrawIconAsync(HWND hWnd, sws_IconPainter_CallbackParams* params) 390 | { 391 | if (IsHungAppWindow(hWnd)) 392 | { 393 | return FALSE; 394 | } 395 | SetTimer(params->hWnd, SWS_WINDOWSWITCHER_TIMER_PAINT, SWS_WINDOWSWITCHER_TIMER_PAINT_GETICONASYNC_DELAY, NULL); 396 | return SendMessageCallbackW(hWnd, WM_GETICON, ICON_BIG, 0, _sws_IconPainter_Callback, params); 397 | } -------------------------------------------------------------------------------- /SimpleWindowSwitcher/sws_IconPainter.h: -------------------------------------------------------------------------------- 1 | #ifndef _H_SWS_ICONPAINTER_ 2 | #define _H_SWS_ICONPAINTER_ 3 | #include 4 | 5 | typedef struct _sws_IconPainter_CallbackParams 6 | { 7 | long long timestamp; 8 | HWND hWnd; 9 | int index; 10 | BOOL bIsDesktop; 11 | BOOL bUseApplicationIcon; 12 | } sws_IconPainter_CallbackParams; 13 | 14 | static void _sws_IconPainter_Callback( 15 | HWND hWnd, 16 | UINT uMsg, 17 | sws_IconPainter_CallbackParams* params, 18 | HICON hIcon 19 | ); 20 | 21 | void sws_IconPainter_DrawIcon(HICON hIcon, HDC hDC, HBRUSH hBrush, void* pGdipGraphics, INT x, INT y, INT w, INT h, RGBQUAD bkcol, BOOL bShouldFillBackground); 22 | 23 | BOOL sws_IconPainter_ExtractAndDrawIconAsync(HWND hWnd, sws_IconPainter_CallbackParams* params); 24 | #endif -------------------------------------------------------------------------------- /SimpleWindowSwitcher/sws_RegistryMonitor.c: -------------------------------------------------------------------------------- 1 | #include "sws_RegistryMonitor.h" 2 | 3 | void sws_RegistryMonitor_Clear(sws_RegistryMonitor* _this) 4 | { 5 | if (_this) 6 | { 7 | CloseHandle(_this->hEvCU); 8 | CloseHandle(_this->hEvLM); 9 | CloseHandle(_this->hKeyCU); 10 | CloseHandle(_this->hKeyLM); 11 | FreeLibrary(_this->hLib); 12 | free(_this->buffer); 13 | free(_this->wszPath); 14 | free(_this->wszValueName); 15 | memset(_this, 0, sizeof(sws_RegistryMonitor)); 16 | } 17 | } 18 | 19 | sws_error_t sws_RegistryMonitor_Notify(sws_RegistryMonitor* _this, DWORD dwWakeMask) 20 | { 21 | HWND hwndMain = NULL; 22 | HWND hwndDlgModeless = NULL; 23 | MSG msg; 24 | BOOL bRet; 25 | HACCEL haccel = NULL; 26 | LSTATUS lRes; 27 | HANDLE hEvents[3]; 28 | hEvents[0] = _this->hEvEx; 29 | hEvents[1] = _this->hEvCU; 30 | hEvents[2] = _this->hEvLM; 31 | while (TRUE) 32 | { 33 | switch (MsgWaitForMultipleObjectsEx( 34 | 3, 35 | hEvents, 36 | INFINITE, 37 | dwWakeMask, 38 | MWMO_INPUTAVAILABLE 39 | )) 40 | { 41 | case WAIT_OBJECT_0 + 0: 42 | return SWS_ERROR_SUCCESS; 43 | case WAIT_OBJECT_0 + 1: 44 | lRes = RegQueryValueExW( 45 | _this->hKeyCU, 46 | _this->wszValueName, 47 | 0, 48 | NULL, 49 | _this->buffer, 50 | &(_this->szBuffer) 51 | ); 52 | if (lRes != ERROR_SUCCESS) 53 | { 54 | return sws_error_Report(sws_error_GetFromWin32Error(GetLastError()), NULL); 55 | } 56 | _this->callback(_this->ptr); 57 | LSTATUS lRes = RegNotifyChangeKeyValue( 58 | _this->hKeyCU, 59 | FALSE, 60 | _this->notifyFilter, 61 | _this->hEvCU, 62 | TRUE 63 | ); 64 | if (lRes != ERROR_SUCCESS && lRes != ERROR_INVALID_HANDLE) 65 | { 66 | return sws_error_Report(sws_error_GetFromWin32Error(lRes), NULL); 67 | } 68 | break; 69 | case WAIT_OBJECT_0 + 2: 70 | lRes = RegQueryValueExW( 71 | _this->hKeyLM, 72 | _this->wszValueName, 73 | 0, 74 | NULL, 75 | _this->buffer, 76 | &(_this->szBuffer) 77 | ); 78 | if (lRes != ERROR_SUCCESS) 79 | { 80 | return sws_error_Report(sws_error_GetFromWin32Error(GetLastError()), NULL); 81 | } 82 | _this->callback(_this->ptr); 83 | lRes = RegNotifyChangeKeyValue( 84 | _this->hKeyLM, 85 | FALSE, 86 | _this->notifyFilter, 87 | _this->hEvLM, 88 | TRUE 89 | ); 90 | if (lRes != ERROR_SUCCESS && lRes != ERROR_INVALID_HANDLE) 91 | { 92 | return sws_error_Report(sws_error_GetFromWin32Error(lRes), NULL); 93 | } 94 | break; 95 | case WAIT_OBJECT_0 + 3: 96 | if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { 97 | TranslateMessage(&msg); 98 | DispatchMessage(&msg); 99 | } 100 | break; 101 | default: 102 | return sws_error_Report(sws_error_GetFromWin32Error(GetLastError()), NULL); 103 | } 104 | } 105 | return SWS_ERROR_SUCCESS; 106 | } 107 | 108 | sws_error_t sws_RegistryMonitor_Initialize( 109 | sws_RegistryMonitor* _this, 110 | wchar_t* wszPath, 111 | wchar_t* wszValueName, 112 | REGSAM desiredAccess, 113 | DWORD notifyFilter, 114 | SRRF srrf, 115 | char* buffer, 116 | size_t szBuffer, 117 | void(*callback)(void* ptr), 118 | void* ptr, 119 | HANDLE hEvEx 120 | ) 121 | { 122 | sws_error_t rv = SWS_ERROR_SUCCESS; 123 | 124 | if (!rv) 125 | { 126 | if (!_this) 127 | { 128 | rv = SWS_ERROR_NO_MEMORY; 129 | } 130 | else 131 | { 132 | memset(_this, 0, sizeof(sws_RegistryMonitor)); 133 | } 134 | _this->ptr = ptr; 135 | } 136 | if (!rv) 137 | { 138 | if (!wszPath) 139 | { 140 | rv = SWS_ERROR_GENERIC_ERROR; 141 | } 142 | _this->lenPath = wcslen(wszPath); 143 | } 144 | if (!rv) 145 | { 146 | _this->wszPath = calloc(1, (_this->lenPath + 1) * sizeof(wchar_t)); 147 | if (!_this->wszPath) 148 | { 149 | rv = SWS_ERROR_NO_MEMORY; 150 | } 151 | } 152 | if (!rv) 153 | { 154 | rv = sws_error_Report(sws_error_GetFromErrno(wcscpy_s(_this->wszPath, _this->lenPath + 1, wszPath)), NULL); 155 | } 156 | if (!rv) 157 | { 158 | if (!wszValueName) 159 | { 160 | rv = SWS_ERROR_GENERIC_ERROR; 161 | } 162 | _this->lenValueName = wcslen(wszValueName); 163 | } 164 | if (!rv) 165 | { 166 | _this->wszValueName = calloc(1, (_this->lenValueName + 1) * sizeof(wchar_t)); 167 | if (!_this->wszValueName) 168 | { 169 | rv = SWS_ERROR_NO_MEMORY; 170 | } 171 | } 172 | if (!rv) 173 | { 174 | rv = sws_error_Report(sws_error_GetFromErrno(wcscpy_s(_this->wszValueName, _this->lenValueName + 1, wszValueName)), NULL); 175 | } 176 | if (!rv) 177 | { 178 | _this->desiredAccess = desiredAccess; 179 | _this->notifyFilter = notifyFilter; 180 | _this->szBuffer = szBuffer; 181 | _this->buffer = calloc(1, szBuffer); 182 | if (!_this->buffer) 183 | { 184 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_NO_MEMORY), NULL); 185 | } 186 | } 187 | if (!rv) 188 | { 189 | _this->callback = callback; 190 | _this->hLib = LoadLibraryW(L"Shlwapi.dll"); 191 | if (!_this->hLib) 192 | { 193 | rv = sws_error_Report(sws_error_GetFromWin32Error(GetLastError()), NULL); 194 | } 195 | } 196 | if (!rv) 197 | { 198 | _this->SHRegGetValueFromHKCUHKLMFunc = GetProcAddress(_this->hLib, "SHRegGetValueFromHKCUHKLM"); 199 | if (!_this->SHRegGetValueFromHKCUHKLMFunc) 200 | { 201 | rv = sws_error_Report(sws_error_GetFromWin32Error(GetLastError()), NULL); 202 | } 203 | } 204 | if (!rv) 205 | { 206 | if (_this->SHRegGetValueFromHKCUHKLMFunc( 207 | wszPath, 208 | wszValueName, 209 | SRRF_RT_REG_DWORD, 210 | NULL, 211 | buffer, 212 | &szBuffer 213 | ) != ERROR_SUCCESS) 214 | { 215 | rv = sws_error_Report(sws_error_GetFromWin32Error(GetLastError()), NULL); 216 | } 217 | } 218 | if (!rv) 219 | { 220 | LSTATUS lRes = RegOpenKeyExW( 221 | HKEY_LOCAL_MACHINE, 222 | wszPath, 223 | 0, 224 | desiredAccess, 225 | &(_this->hKeyLM) 226 | ); 227 | if (lRes != ERROR_SUCCESS && lRes != ERROR_FILE_NOT_FOUND) 228 | { 229 | rv = sws_error_Report(sws_error_GetFromWin32Error(lRes), NULL); 230 | } 231 | } 232 | if (!rv) 233 | { 234 | LSTATUS lRes = RegOpenKeyExW( 235 | HKEY_CURRENT_USER, 236 | wszPath, 237 | 0, 238 | desiredAccess, 239 | &(_this->hKeyCU) 240 | ); 241 | if (lRes != ERROR_SUCCESS && lRes != ERROR_FILE_NOT_FOUND) 242 | { 243 | rv = sws_error_Report(sws_error_GetFromWin32Error(lRes), NULL); 244 | } 245 | } 246 | if (!rv) 247 | { 248 | _this->hEvLM = CreateEvent(NULL, FALSE, FALSE, NULL); 249 | if (!_this->hEvLM) 250 | { 251 | rv = sws_error_Report(sws_error_GetFromWin32Error(GetLastError()), NULL); 252 | } 253 | } 254 | if (!rv) 255 | { 256 | _this->hEvCU = CreateEvent(NULL, FALSE, FALSE, NULL); 257 | if (!_this->hEvCU) 258 | { 259 | rv = sws_error_Report(sws_error_GetFromWin32Error(GetLastError()), NULL); 260 | } 261 | } 262 | if (!rv) 263 | { 264 | _this->hEvEx = hEvEx; 265 | } 266 | if (!rv) 267 | { 268 | LSTATUS lRes = RegNotifyChangeKeyValue( 269 | _this->hKeyLM, 270 | FALSE, 271 | notifyFilter, 272 | _this->hEvLM, 273 | TRUE 274 | ); 275 | if (lRes != ERROR_SUCCESS && lRes != ERROR_INVALID_HANDLE) 276 | { 277 | rv = sws_error_Report(sws_error_GetFromWin32Error(lRes), NULL); 278 | } 279 | } 280 | if (!rv) 281 | { 282 | LSTATUS lRes = RegNotifyChangeKeyValue( 283 | _this->hKeyCU, 284 | FALSE, 285 | notifyFilter, 286 | _this->hEvCU, 287 | TRUE 288 | ); 289 | if (lRes != ERROR_SUCCESS && lRes != ERROR_INVALID_HANDLE) 290 | { 291 | rv = sws_error_Report(sws_error_GetFromWin32Error(lRes), NULL); 292 | } 293 | } 294 | 295 | return rv; 296 | } -------------------------------------------------------------------------------- /SimpleWindowSwitcher/sws_RegistryMonitor.h: -------------------------------------------------------------------------------- 1 | #ifndef _H_REGISTRYMONITOR_ 2 | #define _H_REGISTRYMONITOR_ 3 | #include 4 | #include 5 | #include "sws_error.h" 6 | 7 | typedef struct sws_RegistryMonitor 8 | { 9 | HKEY hKeyLM; 10 | HKEY hKeyCU; 11 | HANDLE hEvLM; 12 | HANDLE hEvCU; 13 | HANDLE hEvEx; 14 | wchar_t* wszPath; 15 | size_t lenPath; 16 | wchar_t* wszValueName; 17 | size_t lenValueName; 18 | REGSAM desiredAccess; 19 | DWORD notifyFilter; 20 | SRRF srrf; 21 | char* buffer; 22 | size_t szBuffer; 23 | void(*callback)(void* ptr); 24 | HANDLE hOp; 25 | HMODULE hLib; 26 | FARPROC SHRegGetValueFromHKCUHKLMFunc; 27 | void* ptr; 28 | } sws_RegistryMonitor; 29 | 30 | sws_error_t sws_RegistryMonitor_Notify(sws_RegistryMonitor* _this, DWORD dwWakeMask); 31 | 32 | void sws_RegistryMonitor_Clear(sws_RegistryMonitor* _this); 33 | 34 | sws_error_t sws_RegistryMonitor_Initialize( 35 | sws_RegistryMonitor* _this, 36 | wchar_t* wszPath, 37 | wchar_t* wszValueName, 38 | REGSAM desiredAccess, 39 | DWORD notifyFilter, 40 | SRRF srrf, 41 | char* buffer, 42 | size_t szBuffer, 43 | void(*callback)(void* ptr), 44 | void* ptr 45 | ); 46 | #endif -------------------------------------------------------------------------------- /SimpleWindowSwitcher/sws_WindowHelpers.c: -------------------------------------------------------------------------------- 1 | #include "sws_WindowHelpers.h" 2 | 3 | ULONG_PTR _sws_gdiplus_token; 4 | RTL_OSVERSIONINFOW sws_global_rovi; 5 | DWORD32 sws_global_ubr; 6 | NtUserBuildHwndList _sws_pNtUserBuildHwndList; 7 | pCreateWindowInBand _sws_CreateWindowInBand; 8 | pSetWindowCompositionAttribute _sws_SetWindowCompositionAttribute; 9 | pIsShellManagedWindow _sws_IsShellManagedWindow; 10 | pIsShellManagedWindow sws_IsShellFrameWindow; 11 | pGetWindowBand _sws_GetWindowBand; 12 | pSetWindowBand _sws_SetWindowBand; 13 | BOOL(*_sws_ShouldSystemUseDarkMode)(); 14 | void(*_sws_RefreshImmersiveColorPolicyState)(); 15 | void(*_sws_SetPreferredAppMode)(INT64); 16 | void(*_sws_AllowDarkModeForWindow)(HWND hWnd, INT64 bAllowDark); 17 | HMODULE _sws_hComctl32 = 0; 18 | HMODULE _sws_hShlwapi = 0; 19 | HMODULE _sws_hWin32u = 0; 20 | HINSTANCE _sws_hUser32 = 0; 21 | HINSTANCE _sws_hUxtheme = 0; 22 | HINSTANCE _sws_hShcore = 0; 23 | pHungWindowFromGhostWindow _sws_HungWindowFromGhostWindow; 24 | pGhostWindowFromHungWindow _sws_GhostWindowFromHungWindow; 25 | pInternalGetWindowIcon _sws_InternalGetWindowIcon; 26 | pIsCoreWindow _sws_IsCoreWindow; 27 | FARPROC sws_SHRegGetValueFromHKCUHKLM; 28 | FARPROC sws_LoadIconWithScaleDown; 29 | BOOL (*sws_SHWindowsPolicy)(REFGUID riid); 30 | DEFINE_GUID(POLID_TurnOffSPIAnimations, 0xD7AF00A, 0xB468, 0x4A39, 0xB0, 0x16, 0x33, 0x3E, 0x22, 0x77, 0xAB, 0xED); 31 | 32 | BOOL sws_WindowHelpers_IsValidMonitor(HMONITOR hMonitor, HDC unnamedParam2, LPRECT unnamedParam3, HMONITOR* pMonitor) 33 | { 34 | if (!pMonitor || !*pMonitor) return FALSE; 35 | if (hMonitor == *pMonitor) 36 | { 37 | *pMonitor = NULL; 38 | return FALSE; 39 | } 40 | return TRUE; 41 | } 42 | 43 | sws_error_t sws_WindowHelpers_PermitDarkMode(HWND hWnd) 44 | { 45 | if (_sws_SetPreferredAppMode && _sws_AllowDarkModeForWindow) 46 | { 47 | _sws_SetPreferredAppMode(TRUE); 48 | if (hWnd) 49 | { 50 | _sws_AllowDarkModeForWindow(hWnd, TRUE); 51 | } 52 | return SWS_ERROR_SUCCESS; 53 | } 54 | else 55 | { 56 | return SWS_ERROR_NOT_INITIALIZED; 57 | } 58 | } 59 | 60 | sws_error_t sws_WindowHelpers_RefreshImmersiveColorPolicyState() 61 | { 62 | if (_sws_RefreshImmersiveColorPolicyState) 63 | { 64 | _sws_RefreshImmersiveColorPolicyState(); 65 | return SWS_ERROR_SUCCESS; 66 | } 67 | else 68 | { 69 | return SWS_ERROR_NOT_INITIALIZED; 70 | } 71 | } 72 | 73 | sws_error_t sws_WindowHelpers_ShouldSystemUseDarkMode(DWORD* dwRes) 74 | { 75 | RTL_OSVERSIONINFOW rovi; 76 | if (sws_WindowHelpers_GetOSVersion(&rovi) && rovi.dwBuildNumber < 18985) 77 | { 78 | *dwRes = TRUE; 79 | return SWS_ERROR_SUCCESS; 80 | } 81 | if (_sws_ShouldSystemUseDarkMode) 82 | { 83 | if (dwRes) 84 | { 85 | *dwRes = _sws_ShouldSystemUseDarkMode(); 86 | return SWS_ERROR_SUCCESS; 87 | } 88 | else 89 | { 90 | return SWS_ERROR_INVALID_PARAMETER; 91 | } 92 | } 93 | else 94 | { 95 | return SWS_ERROR_NOT_INITIALIZED; 96 | } 97 | } 98 | 99 | sws_error_t sws_WindowHelpers_SetWindowBlur(HWND hWnd, int type, DWORD Color, DWORD Opacity) 100 | { 101 | ACCENTPOLICY policy; 102 | policy.nAccentState = type; 103 | policy.nFlags = 0; 104 | policy.nColor = (Opacity << 24) | (Color & 0xFFFFFF); // ACCENT_ENABLE_BLURBEHIND=3... // Color = 0XB32E9A 105 | policy.nFlags = 0; 106 | WINCOMPATTRDATA data = { 19, &policy, sizeof(ACCENTPOLICY) }; // WCA_ACCENT_POLICY=19 107 | if (_sws_SetWindowCompositionAttribute) 108 | { 109 | _sws_SetWindowCompositionAttribute(hWnd, &data); 110 | } 111 | else 112 | { 113 | return SWS_ERROR_NOT_INITIALIZED; 114 | } 115 | } 116 | 117 | HWND* _sws_WindowHelpers_Gui_BuildWindowList 118 | ( 119 | NtUserBuildHwndList pNtUserBuildHwndList, 120 | HDESK in_hDesk, 121 | HWND in_hWnd, 122 | BOOL in_EnumChildren, 123 | BOOL in_RemoveImmersive, 124 | UINT in_ThreadID, 125 | INT* out_Cnt 126 | ) 127 | { 128 | /* locals */ 129 | UINT lv_Max; 130 | UINT lv_Cnt; 131 | UINT lv_NtStatus; 132 | HWND* lv_List; 133 | 134 | // initial size of list 135 | lv_Max = 512; 136 | 137 | // retry to get list 138 | for (;;) 139 | { 140 | // allocate list 141 | if ((lv_List = (HWND*)malloc(lv_Max * sizeof(HWND))) == NULL) 142 | break; 143 | 144 | // call the api 145 | lv_NtStatus = pNtUserBuildHwndList( 146 | in_hDesk, in_hWnd, 147 | in_EnumChildren, in_RemoveImmersive, in_ThreadID, 148 | lv_Max, lv_List, &lv_Cnt); 149 | 150 | // success? 151 | if (lv_NtStatus == NOERROR) 152 | break; 153 | 154 | // free allocated list 155 | free(lv_List); 156 | 157 | // clear 158 | lv_List = NULL; 159 | 160 | // other error then buffersize? or no increase in size? 161 | if (lv_NtStatus != STATUS_BUFFER_TOO_SMALL || lv_Cnt <= lv_Max) 162 | break; 163 | 164 | // update max plus some extra to take changes in number of windows into account 165 | lv_Max = lv_Cnt + 16; 166 | } 167 | 168 | // return the count 169 | *out_Cnt = lv_Cnt; 170 | 171 | // return the list, or NULL when failed 172 | return lv_List; 173 | } 174 | 175 | static void _sws_WindowHelpers_FindWindowExReverseOrder(HWND hWnd, WNDENUMPROC in_Proc, LPARAM in_Param) 176 | { 177 | hWnd = FindWindowEx(NULL, hWnd, NULL, NULL); 178 | if (!hWnd) 179 | { 180 | return; 181 | } 182 | test(hWnd, in_Proc, in_Param); 183 | if (!in_Proc(hWnd, in_Param)) 184 | return; 185 | } 186 | 187 | /********************************************************/ 188 | /* enumerate all top level windows including metro apps */ 189 | /********************************************************/ 190 | sws_error_t sws_WindowHelpers_RealEnumWindows( 191 | WNDENUMPROC in_Proc, 192 | LPARAM in_Param 193 | ) 194 | { 195 | if (!_sws_pNtUserBuildHwndList) 196 | { 197 | return sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_NOT_INITIALIZED), NULL); 198 | } 199 | 200 | /* locals */ 201 | INT lv_Cnt; 202 | HWND lv_hWnd; 203 | BOOL lv_Result; 204 | HWND lv_hFirstWnd; 205 | HWND lv_hDeskWnd; 206 | HWND* lv_List; 207 | 208 | // no error yet 209 | lv_Result = TRUE; 210 | 211 | // first try api to get full window list including immersive/metro apps 212 | lv_List = _sws_WindowHelpers_Gui_BuildWindowList(_sws_pNtUserBuildHwndList, 0, 0, 0, 0, 0, &lv_Cnt); 213 | 214 | // success? 215 | if (lv_List) 216 | { 217 | // loop through list 218 | while (lv_Cnt-- > 0 && lv_Result) 219 | { 220 | // get handle 221 | lv_hWnd = lv_List[lv_Cnt]; 222 | 223 | // filter out the invalid entry (0x00000001) then call the callback 224 | if (IsWindow(lv_hWnd)) 225 | lv_Result = in_Proc(lv_hWnd, in_Param); 226 | } 227 | 228 | // free the list 229 | free(lv_List); 230 | } 231 | else 232 | { 233 | // get desktop window, this is equivalent to specifying NULL as hwndParent 234 | lv_hDeskWnd = GetDesktopWindow(); 235 | 236 | // fallback to using FindWindowEx, get first top-level window 237 | lv_hFirstWnd = FindWindowEx(lv_hDeskWnd, 0, 0, 0); 238 | 239 | // init the enumeration 240 | lv_Cnt = 0; 241 | lv_hWnd = lv_hFirstWnd; 242 | 243 | // loop through windows found 244 | // - since 2012 the EnumWindows API in windows has a problem (on purpose by MS) 245 | // that it does not return all windows (no metro apps, no start menu etc) 246 | // - luckally the FindWindowEx() still is clean and working 247 | while (lv_hWnd && lv_Result) 248 | { 249 | // call the callback 250 | lv_Result = in_Proc(lv_hWnd, in_Param); 251 | 252 | // get next window 253 | lv_hWnd = FindWindowEx(lv_hDeskWnd, lv_hWnd, 0, 0); 254 | 255 | // protect against changes in window hierachy during enumeration 256 | if (lv_hWnd == lv_hFirstWnd || lv_Cnt++ > 10000) 257 | break; 258 | } 259 | } 260 | 261 | // return the result 262 | //return lv_Result; 263 | return SWS_ERROR_SUCCESS; 264 | } 265 | 266 | BOOL _sws_WindowHelpers_ShouldTreatShellManagedWindowAsNotShellManaged(HWND hWnd) 267 | { 268 | return (GetPropW(hWnd, L"Microsoft.Windows.ShellManagedWindowAsNormalWindow") != NULL); 269 | } 270 | 271 | BOOL _sws_WindowHelpers_IsWindowA920(HWND hWnd) 272 | { 273 | return GetPropW(hWnd, (LPCWSTR)0xA920); 274 | } 275 | 276 | BOOL _sws_WindowHelpers_IsValidDesktopZOrderBand(HWND hWnd, int bAdditionalChecks) 277 | { 278 | BOOL bRet = FALSE; 279 | DWORD dwBand = ZBID_DEFAULT; 280 | if (_sws_GetWindowBand && _sws_GetWindowBand(hWnd, &dwBand)) 281 | { 282 | wchar_t wszWindowText[200]; 283 | GetWindowTextW(hWnd, wszWindowText, 200); 284 | bRet = _sws_IsBandValidToInclude[dwBand]; 285 | if (_sws_WindowHelpers_IsWindowA920(hWnd)) 286 | { 287 | bRet = TRUE; 288 | } 289 | else if (!bRet) 290 | { 291 | return bRet; 292 | } 293 | if (bAdditionalChecks) 294 | { 295 | //wprintf(L"%s %d %d\n", wszWindowText, dwBand, _sws_IsShellManagedWindow(hWnd)); 296 | return !(_sws_IsShellManagedWindow && _sws_IsShellManagedWindow(hWnd)) || _sws_WindowHelpers_ShouldTreatShellManagedWindowAsNotShellManaged(hWnd) || sws_WindowHelpers_IsWindowUWP(hWnd); 297 | } 298 | } 299 | return bRet; 300 | } 301 | 302 | BOOL _sws_WindowHelpers_IsWindowNotDesktopOrTrayHelper(HWND hWnd) 303 | { 304 | ATOM SecondaryTaskbarAtom = RegisterWindowMessageW(L"Shell_SecondaryTrayWnd"); 305 | if (GetClassWord(hWnd, GCW_ATOM) == SecondaryTaskbarAtom) 306 | { 307 | return (SecondaryTaskbarAtom == 0); 308 | } 309 | return TRUE; 310 | } 311 | 312 | BOOL _sws_WindowHelpers_IsWindowNotDesktopOrTray(HWND hWnd, HWND hWndDesktop) 313 | { 314 | if (IsWindow(hWnd) && 315 | hWnd != FindWindowW(L"Shell_TrayWnd", NULL) && 316 | hWnd != hWndDesktop) 317 | { 318 | return _sws_WindowHelpers_IsWindowNotDesktopOrTrayHelper(hWnd); 319 | } 320 | return FALSE; 321 | } 322 | 323 | BOOL _sws_WindowHelpers_IsValidTabWindowForTray(HWND hWnd) 324 | { 325 | if (!hWnd) 326 | { 327 | return FALSE; 328 | } 329 | if (!_sws_WindowHelpers_IsWindowA920(hWnd)) 330 | { 331 | return TRUE; 332 | } 333 | IPropertyStore* propStore = NULL; 334 | SHGetPropertyStoreForWindow( 335 | hWnd, 336 | &__uuidof_IPropertyStore, 337 | &propStore 338 | ); 339 | if (propStore) 340 | { 341 | PROPERTYKEY pKey; 342 | pKey.fmtid = __uuidof_AppUserModelIdProperty; 343 | pKey.pid = 5; 344 | PROPVARIANT prop; 345 | ZeroMemory(&prop, sizeof(PROPVARIANT)); 346 | if (SUCCEEDED(propStore->lpVtbl->GetValue(propStore, &pKey, &prop))) PropVariantClear(&prop); 347 | propStore->lpVtbl->Release(propStore); 348 | IShellItem2* item; 349 | HRESULT hr = SHCreateItemInKnownFolder( 350 | &FOLDERID_AppsFolder, 351 | KF_FLAG_DONT_VERIFY, 352 | prop.bstrVal, 353 | &__uuidof_IShellItem2, 354 | &item 355 | ); 356 | if (SUCCEEDED(hr)) 357 | { 358 | item->lpVtbl->Release(item); 359 | return TRUE; 360 | } 361 | } 362 | return FALSE; 363 | } 364 | 365 | BOOL _sws_WindowHelpers_ShouldAddWindowToTrayHelper(HWND hWnd) 366 | { 367 | LONG WinExStyle = GetWindowLong(hWnd, GWL_EXSTYLE); 368 | if (_sws_WindowHelpers_IsValidTabWindowForTray(hWnd) && 369 | (GetWindow(hWnd, GW_OWNER) == NULL || (WinExStyle & WS_EX_APPWINDOW)) && 370 | !(WinExStyle & WS_EX_TOOLWINDOW)) 371 | { 372 | return TRUE; 373 | } 374 | return FALSE; 375 | } 376 | 377 | BOOL _sws_TestExStyle(HWND hWnd, DWORD dwExStyle) 378 | { 379 | return dwExStyle == (dwExStyle & (DWORD)GetWindowLongPtrW(hWnd, GWL_EXSTYLE)); 380 | } 381 | 382 | BOOLEAN _sws_IsOwnerToolWindow(HWND hwnd) 383 | { 384 | BOOLEAN bRet = FALSE; 385 | 386 | HWND hwndCurrent = hwnd; 387 | HWND hwndOwner = GetWindow(hwnd, GW_OWNER); 388 | while (!_sws_TestExStyle(hwndCurrent, WS_EX_APPWINDOW) && hwndOwner) 389 | { 390 | HWND hwndPrev = hwndCurrent; 391 | hwndCurrent = hwndOwner; 392 | hwndOwner = GetWindow(hwndOwner, GW_OWNER); 393 | if (_sws_TestExStyle(hwndCurrent, WS_EX_TOOLWINDOW)) 394 | { 395 | bRet = !_sws_TestExStyle(hwndPrev, WS_EX_CONTROLPARENT) || hwndOwner != NULL; 396 | break; 397 | } 398 | } 399 | 400 | return bRet; 401 | } 402 | 403 | BOOL _sws_IsReallyVisible(HWND hWnd) 404 | { 405 | RECT rc; 406 | GetWindowRect(hWnd, &rc); 407 | return IsWindowVisible(hWnd) && !IsRectEmpty(&rc); 408 | } 409 | 410 | BOOL _sws_IsGhosted(HWND hwnd) 411 | { 412 | return _sws_GhostWindowFromHungWindow(hwnd) != NULL; 413 | } 414 | 415 | BOOL _sws_ShouldListWindowInAltTab(HWND hwnd) 416 | { 417 | BOOL bRet = FALSE; 418 | 419 | if (IsWindow(hwnd) /*&& hwnd != _hwnd*/) 420 | { 421 | DWORD dwExStyle = (DWORD)GetWindowLongPtrW(hwnd, GWL_EXSTYLE); 422 | HWND hwndOwner = GetWindow(hwnd, GW_OWNER); 423 | BOOLEAN bOwnerVisible = IsWindow(hwndOwner) && IsWindowEnabled(hwndOwner) && _sws_IsReallyVisible(hwndOwner); 424 | BOOLEAN bNoActivate = (dwExStyle & WS_EX_NOACTIVATE) != 0 || (dwExStyle & WS_EX_TOOLWINDOW) != 0; 425 | BOOLEAN bAppWindow = (dwExStyle & WS_EX_APPWINDOW) != 0; 426 | if (bAppWindow) 427 | { 428 | bNoActivate = FALSE; 429 | } 430 | bRet = _sws_IsReallyVisible(hwnd) 431 | // && IsWindowEnabled(hwnd) 432 | && !bNoActivate 433 | && (bAppWindow || !bOwnerVisible && !_sws_IsOwnerToolWindow(hwnd)) 434 | && !_sws_IsGhosted(hwnd); 435 | } 436 | 437 | return bRet; 438 | } 439 | 440 | BOOL _sws__IsTaskWindow(HWND hwnd) 441 | { 442 | DWORD dwExStyle = (DWORD)GetWindowLongPtrW(hwnd, GWL_EXSTYLE); 443 | return ((dwExStyle & WS_EX_APPWINDOW) != 0 || ((dwExStyle & WS_EX_TOOLWINDOW) == 0 && (dwExStyle & WS_EX_NOACTIVATE) == 0)) 444 | && IsWindowVisible(hwnd) 445 | && !_sws_IsGhosted(hwnd); 446 | } 447 | 448 | BOOL _sws_IsTaskWindow(HWND hwnd, HWND* phwndTaskWindow) 449 | { 450 | BOOL bRet = FALSE; 451 | 452 | if (_sws_ShouldListWindowInAltTab(hwnd)) 453 | { 454 | HWND hwndTaskWindow = hwnd; 455 | HWND hwndCurrent = hwnd; 456 | while ((hwndCurrent = GetWindow(hwndCurrent, GW_OWNER))) 457 | { 458 | if (!_sws__IsTaskWindow(hwndCurrent)) 459 | break; 460 | hwndTaskWindow = hwndCurrent; 461 | } 462 | *phwndTaskWindow = hwndTaskWindow; 463 | bRet = TRUE; 464 | } 465 | 466 | return bRet; 467 | } 468 | 469 | wchar_t* sws_WindowHelpers_GetAUMIDForHWND(HWND hWnd) 470 | { 471 | WCHAR* pszAppId; 472 | if (SUCCEEDED(sws_AppResolver->lpVtbl->GetAppIDForWindow(sws_AppResolver, hWnd, &pszAppId, NULL, NULL, NULL)) && pszAppId) return pszAppId; 473 | return NULL; 474 | } 475 | 476 | BOOL sws_WindowHelpers_IsTaskbarWindow(HWND hWnd, HWND hWndWallpaper) 477 | { 478 | if (_sws_WindowHelpers_IsValidDesktopZOrderBand(hWnd, TRUE) && 479 | _sws_WindowHelpers_IsWindowNotDesktopOrTray(hWnd, hWndWallpaper) && 480 | IsWindowVisible(hWnd) && 481 | !_sws_HungWindowFromGhostWindow(hWnd)) 482 | { 483 | return _sws_WindowHelpers_ShouldAddWindowToTrayHelper(hWnd); 484 | } 485 | return FALSE; 486 | } 487 | 488 | BOOL sws_WindowHelpers_IsWindowShellManagedByExplorerPatcher(HWND hWnd) 489 | { 490 | return GetPropW(hWnd, L"valinet.ExplorerPatcher.ShellManagedWindow") != 0; 491 | } 492 | 493 | BOOL sws_WindowHelpers_ShouldTreatShellManagedWindowAsNotShellManaged(HWND hWnd) 494 | { 495 | return GetPropW(hWnd, L"Microsoft.Windows.ShellManagedWindowAsNormalWindow") != 0; 496 | } 497 | 498 | BOOL sws_WindowHelpers_IsAltTabWindow(HWND hWnd) 499 | { 500 | // This identifies whether a window is a shell frame and includes those 501 | // A shell frame corresponds to, as far as I can tell, the frame of a UWP app 502 | // and we want those in the Alt-Tab list 503 | // Bugfix: Exclude hung shell frame (immersive) UWP windows, as we already include 504 | // ghost app windows in their place already 505 | if (sws_IsShellFrameWindow(hWnd) && !_sws_GhostWindowFromHungWindow(hWnd)) 506 | { 507 | return TRUE; 508 | } 509 | // Next, we need to check whether the window is shell managed and exclude it if so 510 | // Shell managed windows, as far as I can tell, represent all immersive UI the 511 | // Windows shell might present the user with, like: Start menu, Search (Win+Q), 512 | // notifications, taskbars etc 513 | if (_sws_IsShellManagedWindow(hWnd) && !sws_WindowHelpers_ShouldTreatShellManagedWindowAsNotShellManaged(hWnd)) 514 | { 515 | return FALSE; 516 | } 517 | // Also, exclude some windows created by ExplorerPatcher 518 | if (sws_WindowHelpers_IsWindowShellManagedByExplorerPatcher(hWnd)) 519 | { 520 | return FALSE; 521 | } 522 | // Lastly, this check works with the remaining classic window and determines if it is a 523 | // "task window" and only includes it in Alt-Tab if so; this check is taken from 524 | // "AltTab.dll" in Windows 7 and this is how that OS decided to include a window in its 525 | // window switcher 526 | HWND hwndTaskWindow = NULL; 527 | return _sws_IsTaskWindow(hWnd, &hwndTaskWindow); 528 | } 529 | 530 | void sws_WindowHelpers_GetDesktopText(wchar_t* wszTitle) 531 | { 532 | HANDLE hExplorerFrame = GetModuleHandleW(L"ExplorerFrame.dll"); 533 | if (hExplorerFrame) 534 | { 535 | LoadStringW(hExplorerFrame, 13140, wszTitle, MAX_PATH); 536 | } 537 | else 538 | { 539 | wcscat_s(wszTitle, MAX_PATH, L"Desktop"); 540 | } 541 | } 542 | 543 | static BOOL CALLBACK _sws_WindowHelpers_GetWallpaperHWNDCallback(HWND hwnd, LPARAM lParam) 544 | { 545 | HWND* ret = (HWND*)lParam; 546 | 547 | HWND p = FindWindowExW(hwnd, NULL, L"SHELLDLL_DefView", NULL); 548 | if (p) 549 | { 550 | HWND t = FindWindowExW(NULL, hwnd, L"WorkerW", NULL); 551 | if (t) 552 | { 553 | *ret = t; 554 | } 555 | } 556 | return TRUE; 557 | } 558 | 559 | BOOL sws_WindowHelpers_EnsureWallpaperHWND() 560 | { 561 | BOOL bRet = FALSE; 562 | 563 | // See: https://github.com/valinet/ExplorerPatcher/issues/525 564 | HWND hProgman = GetShellWindow(); 565 | if (hProgman) 566 | { 567 | DWORD_PTR res0 = -1, res1 = -1, res2 = -1, res3 = -1; 568 | // CDesktopBrowser::_IsDesktopWallpaperInitialized and CWallpaperRenderer::ExpireImages 569 | SendMessageTimeoutW(hProgman, 0x052C, 10, 0, SMTO_NORMAL, 1000, &res0); 570 | if (SUCCEEDED(res0)) 571 | { 572 | // Generate wallpaper window 573 | SendMessageTimeoutW(hProgman, 0x052C, 13, 0, SMTO_NORMAL, 1000, &res1); 574 | SendMessageTimeoutW(hProgman, 0x052C, 13, 1, SMTO_NORMAL, 1000, &res2); 575 | SendMessageTimeoutW(hProgman, 0x052C, 0, 0, SMTO_NORMAL, 1000, &res3); 576 | bRet = !res1 && !res2 && !res3; 577 | } 578 | } 579 | 580 | return bRet; 581 | } 582 | 583 | HWND sws_WindowHelpers_GetWallpaperHWND() 584 | { 585 | HWND hWnd = NULL; 586 | 587 | // 24H2 has the Progman window as the whole desktop (icons + wallpaper) 588 | // Check if SHELLDLL_DefView is a direct child of Progman 589 | HWND hWndProgman = GetShellWindow(); 590 | if (hWndProgman) 591 | { 592 | HWND hWndDefView = FindWindowExW(hWndProgman, NULL, L"SHELLDLL_DefView", NULL); 593 | if (hWndDefView) 594 | { 595 | hWnd = hWndProgman; // We're running on 24H2 or newer 596 | } 597 | } 598 | 599 | // Otherwise find a top-level WorkerW window with a SHELLDLL_DefView child, that's our desktop icons 600 | if (!hWnd) 601 | { 602 | EnumWindows(_sws_WindowHelpers_GetWallpaperHWNDCallback, &hWnd); 603 | } 604 | 605 | return hWnd; 606 | } 607 | 608 | BOOL CALLBACK sws_WindowHelpers_AddAltTabWindowsToTimeStampedHWNDList(HWND hWnd, HDPA hdpa) 609 | { 610 | if (!hdpa) 611 | { 612 | return FALSE; 613 | } 614 | if (sws_WindowHelpers_IsAltTabWindow(hWnd)) 615 | { 616 | sws_tshwnd* tshWnd = malloc(sizeof(sws_tshwnd)); 617 | if (tshWnd) 618 | { 619 | sws_tshwnd_Initialize(tshWnd, hWnd); 620 | sws_tshwnd_ModifyTimestamp(tshWnd, sws_WindowHelpers_GetStartTime()); 621 | DPA_AppendPtr(hdpa, tshWnd); 622 | } 623 | } 624 | return TRUE; 625 | } 626 | 627 | // https://stackoverflow.com/questions/5309914/updatelayeredwindow-and-drawtext 628 | HBITMAP sws_WindowHelpers_CreateAlphaTextBitmap(LPCWSTR inText, HFONT inFont, DWORD dwTextFlags, SIZE size, COLORREF inColour) 629 | { 630 | // Create DC and select font into it 631 | HDC hTextDC = CreateCompatibleDC(NULL); 632 | HFONT hOldFont = (HFONT)SelectObject(hTextDC, inFont); 633 | HBITMAP hMyDIB = NULL; 634 | 635 | // Get text area 636 | RECT TextArea = { 0, 0, size.cx, size.cy }; 637 | if ((TextArea.right > TextArea.left) && (TextArea.bottom > TextArea.top)) 638 | { 639 | BITMAPINFOHEADER BMIH; 640 | memset(&BMIH, 0x0, sizeof(BITMAPINFOHEADER)); 641 | void* pvBits = NULL; 642 | 643 | // Specify DIB setup 644 | BMIH.biSize = sizeof(BMIH); 645 | BMIH.biWidth = TextArea.right - TextArea.left; 646 | BMIH.biHeight = TextArea.bottom - TextArea.top; 647 | BMIH.biPlanes = 1; 648 | BMIH.biBitCount = 32; 649 | BMIH.biCompression = BI_RGB; 650 | 651 | // Create and select DIB into DC 652 | hMyDIB = CreateDIBSection(hTextDC, (LPBITMAPINFO)&BMIH, 0, (LPVOID*)&pvBits, NULL, 0); 653 | HBITMAP hOldBMP = (HBITMAP)SelectObject(hTextDC, hMyDIB); 654 | if (hOldBMP != NULL) 655 | { 656 | // Set up DC properties 657 | SetTextColor(hTextDC, 0x00FFFFFF); 658 | SetBkColor(hTextDC, 0x00000000); 659 | SetBkMode(hTextDC, OPAQUE); 660 | 661 | // Draw text to buffer 662 | DrawTextW(hTextDC, inText, -1, &TextArea, dwTextFlags); 663 | BYTE* DataPtr = (BYTE*)pvBits; 664 | BYTE FillR = GetRValue(inColour); 665 | BYTE FillG = GetGValue(inColour); 666 | BYTE FillB = GetBValue(inColour); 667 | BYTE ThisA; 668 | for (int LoopY = 0; LoopY < BMIH.biHeight; LoopY++) { 669 | for (int LoopX = 0; LoopX < BMIH.biWidth; LoopX++) { 670 | ThisA = *DataPtr; // Move alpha and pre-multiply with RGB 671 | *DataPtr++ = (FillB * ThisA) >> 8; 672 | *DataPtr++ = (FillG * ThisA) >> 8; 673 | *DataPtr++ = (FillR * ThisA) >> 8; 674 | *DataPtr++ = ThisA; // Set Alpha 675 | } 676 | } 677 | 678 | // De-select bitmap 679 | SelectObject(hTextDC, hOldBMP); 680 | } 681 | } 682 | 683 | // De-select font and destroy temp DC 684 | SelectObject(hTextDC, hOldFont); 685 | DeleteDC(hTextDC); 686 | 687 | // Return DIBSection 688 | return hMyDIB; 689 | } 690 | 691 | BOOL sws_WindowHelpers_AreAnimationsAllowed() 692 | { 693 | if (sws_SHWindowsPolicy(&POLID_TurnOffSPIAnimations)) 694 | { 695 | return FALSE; 696 | } 697 | 698 | BOOL bAnimationsEnabled = FALSE; 699 | SystemParametersInfoW(SPI_GETCLIENTAREAANIMATION, 0, &bAnimationsEnabled, 0); 700 | return bAnimationsEnabled; 701 | } 702 | 703 | void sws_WindowHelpers_GetWindowText(HWND hWnd, LPCWSTR lpWStr, DWORD dwLength) 704 | { 705 | HWND hWndGhost = _sws_GhostWindowFromHungWindow(hWnd); 706 | if (hWndGhost) 707 | { 708 | sws_InternalGetWindowText(hWndGhost, lpWStr, dwLength); 709 | } 710 | else 711 | { 712 | sws_InternalGetWindowText(hWnd, lpWStr, dwLength); 713 | } 714 | } 715 | 716 | HWND sws_WindowHelpers_GetLastActivePopup(HWND hWnd) 717 | { 718 | HWND hOwner = GetWindow(hWnd, GW_OWNER); 719 | return GetLastActivePopup(hOwner ? hOwner : hWnd); 720 | } 721 | 722 | void sws_WindowHelpers_Clear() 723 | { 724 | GdiplusShutdown(_sws_gdiplus_token); 725 | _sws_gdiplus_token = 0; 726 | if (sws_DefAppIcon) 727 | { 728 | DestroyIcon(sws_DefAppIcon); 729 | sws_DefAppIcon = NULL; 730 | } 731 | if (_sws_hWin32u) 732 | { 733 | FreeLibrary(_sws_hWin32u); 734 | _sws_hWin32u = NULL; 735 | } 736 | if (_sws_hUser32) 737 | { 738 | FreeLibrary(_sws_hUser32); 739 | _sws_hUser32 = NULL; 740 | } 741 | if (_sws_hUxtheme) 742 | { 743 | FreeLibrary(_sws_hUxtheme); 744 | _sws_hUxtheme = NULL; 745 | } 746 | if (_sws_hShlwapi) 747 | { 748 | FreeLibrary(_sws_hShlwapi); 749 | _sws_hShlwapi = NULL; 750 | } 751 | if (_sws_hComctl32) 752 | { 753 | FreeLibrary(_sws_hComctl32); 754 | _sws_hComctl32 = NULL; 755 | } 756 | if (_sws_hShcore) 757 | { 758 | FreeLibrary(_sws_hShcore); 759 | _sws_hShcore = NULL; 760 | } 761 | if (sws_AppResolver) 762 | { 763 | sws_AppResolver->lpVtbl->Release(sws_AppResolver); 764 | sws_AppResolver = NULL; 765 | } 766 | } 767 | 768 | sws_error_t sws_WindowHelpers_Initialize() 769 | { 770 | sws_error_t rv = SWS_ERROR_SUCCESS; 771 | 772 | if (_sws_gdiplus_token) 773 | { 774 | return rv; 775 | } 776 | sws_global_ubr = sws_WindowHelpers_GetOSVersionAndUBR(&sws_global_rovi); 777 | if (sws_global_rovi.dwMajorVersion == 0) 778 | { 779 | return SWS_ERROR_GENERIC_ERROR; 780 | } 781 | GetSystemTimeAsFileTime(&sws_ancient_ft); 782 | GetSystemTimeAsFileTime(&sws_start_ft); 783 | if (!rv) 784 | { 785 | _sws_hComctl32 = LoadLibraryW(L"Comctl32.dll"); 786 | if (!_sws_hComctl32) 787 | { 788 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_LOADLIBRARY_FAILED), NULL); 789 | } 790 | } 791 | if (!rv) 792 | { 793 | sws_LoadIconWithScaleDown = GetProcAddress(_sws_hComctl32, "LoadIconWithScaleDown"); 794 | if (!sws_LoadIconWithScaleDown) 795 | { 796 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_FUNCTION_NOT_FOUND), NULL); 797 | } 798 | } 799 | if (!rv) 800 | { 801 | sws_LoadIconWithScaleDown( 802 | (HINSTANCE)NULL, 803 | (PCWSTR)MAKEINTRESOURCEW(IDI_APPLICATION), 804 | (int)128, 805 | (int)128, 806 | (HICON*)(&(sws_DefAppIcon)) 807 | ); 808 | sws_LoadIconWithScaleDown( 809 | (HINSTANCE)NULL, 810 | (PCWSTR)MAKEINTRESOURCEW(IDI_APPLICATION), 811 | (int)128, 812 | (int)128, 813 | (HICON*)(&(sws_LegacyDefAppIcon)) 814 | ); 815 | //sws_LegacyDefAppIcon = LoadIconW(NULL, MAKEINTRESOURCEW(IDI_APPLICATION)); 816 | } 817 | if (!rv) 818 | { 819 | UINT32 gdiplusStartupInput[100]; 820 | ZeroMemory(gdiplusStartupInput, 100); 821 | gdiplusStartupInput[0] = 1; 822 | rv = sws_error_Report(sws_error_GetFromGdiplusStatus(GdiplusStartup(&_sws_gdiplus_token, &gdiplusStartupInput)), NULL); 823 | } 824 | if (!rv) 825 | { 826 | _sws_hShlwapi = LoadLibraryW(L"Shlwapi.dll"); 827 | if (!_sws_hShlwapi) 828 | { 829 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_LOADLIBRARY_FAILED), NULL); 830 | } 831 | } 832 | if (!rv) 833 | { 834 | sws_SHRegGetValueFromHKCUHKLM = GetProcAddress(_sws_hShlwapi, "SHRegGetValueFromHKCUHKLM"); 835 | if (!sws_SHRegGetValueFromHKCUHKLM) 836 | { 837 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_FUNCTION_NOT_FOUND), NULL); 838 | } 839 | 840 | } 841 | if (!rv) 842 | { 843 | if (!_sws_hWin32u) 844 | { 845 | _sws_hWin32u = LoadLibraryW(L"win32u.dll"); 846 | if (!_sws_hWin32u) 847 | { 848 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_LOADLIBRARY_FAILED), NULL); 849 | } 850 | } 851 | } 852 | if (!rv) 853 | { 854 | if (!_sws_pNtUserBuildHwndList) 855 | { 856 | _sws_pNtUserBuildHwndList = (NtUserBuildHwndList)GetProcAddress(_sws_hWin32u, "NtUserBuildHwndList"); 857 | if (!_sws_pNtUserBuildHwndList) 858 | { 859 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_FUNCTION_NOT_FOUND), NULL); 860 | } 861 | } 862 | } 863 | if (!rv) 864 | { 865 | if (!_sws_hUser32) 866 | { 867 | _sws_hUser32 = LoadLibraryW(L"user32.dll"); 868 | if (!_sws_hUser32) 869 | { 870 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_LOADLIBRARY_FAILED), NULL); 871 | } 872 | } 873 | } 874 | if (!rv) 875 | { 876 | if (!_sws_HungWindowFromGhostWindow) 877 | { 878 | _sws_HungWindowFromGhostWindow = (pHungWindowFromGhostWindow)GetProcAddress(_sws_hUser32, "HungWindowFromGhostWindow"); 879 | if (!_sws_HungWindowFromGhostWindow) 880 | { 881 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_FUNCTION_NOT_FOUND), NULL); 882 | } 883 | } 884 | } 885 | if (!rv) 886 | { 887 | if (!_sws_GhostWindowFromHungWindow) 888 | { 889 | _sws_GhostWindowFromHungWindow = (pGhostWindowFromHungWindow)GetProcAddress(_sws_hUser32, "GhostWindowFromHungWindow"); 890 | if (!_sws_GhostWindowFromHungWindow) 891 | { 892 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_FUNCTION_NOT_FOUND), NULL); 893 | } 894 | } 895 | } 896 | if (!rv) 897 | { 898 | if (!_sws_SetWindowCompositionAttribute) 899 | { 900 | _sws_SetWindowCompositionAttribute = (pSetWindowCompositionAttribute)GetProcAddress(_sws_hUser32, "SetWindowCompositionAttribute"); 901 | if (!_sws_SetWindowCompositionAttribute) 902 | { 903 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_FUNCTION_NOT_FOUND), NULL); 904 | } 905 | } 906 | } 907 | if (!rv) 908 | { 909 | if (!_sws_IsShellManagedWindow) 910 | { 911 | _sws_IsShellManagedWindow = (pSetWindowCompositionAttribute)GetProcAddress(_sws_hUser32, (LPCSTR)2574); 912 | if (!_sws_IsShellManagedWindow) 913 | { 914 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_FUNCTION_NOT_FOUND), NULL); 915 | } 916 | } 917 | } 918 | if (!rv) 919 | { 920 | if (!sws_IsShellFrameWindow) 921 | { 922 | sws_IsShellFrameWindow = (pSetWindowCompositionAttribute)GetProcAddress(_sws_hUser32, (LPCSTR)2573); 923 | if (!sws_IsShellFrameWindow) 924 | { 925 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_FUNCTION_NOT_FOUND), NULL); 926 | } 927 | } 928 | } 929 | if (!rv) 930 | { 931 | if (!_sws_CreateWindowInBand) 932 | { 933 | _sws_CreateWindowInBand = (pCreateWindowInBand)GetProcAddress(_sws_hUser32, "CreateWindowInBand"); 934 | if (!_sws_CreateWindowInBand) 935 | { 936 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_FUNCTION_NOT_FOUND), NULL); 937 | } 938 | } 939 | } 940 | if (!rv) 941 | { 942 | if (!_sws_GetWindowBand) 943 | { 944 | _sws_GetWindowBand = GetProcAddress(_sws_hUser32, "GetWindowBand"); 945 | if (!_sws_GetWindowBand) 946 | { 947 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_FUNCTION_NOT_FOUND), NULL); 948 | } 949 | } 950 | } 951 | if (!rv) 952 | { 953 | if (!_sws_SetWindowBand) 954 | { 955 | _sws_SetWindowBand = GetProcAddress(_sws_hUser32, "SetWindowBand"); 956 | if (!_sws_SetWindowBand) 957 | { 958 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_FUNCTION_NOT_FOUND), NULL); 959 | } 960 | } 961 | } 962 | if (!rv) 963 | { 964 | if (!_sws_IsTopLevelWindow) 965 | { 966 | _sws_IsTopLevelWindow = GetProcAddress(_sws_hUser32, "IsTopLevelWindow"); 967 | if (!_sws_IsTopLevelWindow) 968 | { 969 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_FUNCTION_NOT_FOUND), NULL); 970 | } 971 | } 972 | } 973 | /*if (!rv) 974 | { 975 | if (!_sws_GetProgmanWindow) 976 | { 977 | _sws_GetProgmanWindow = GetProcAddress(_sws_hUser32, "GetProgmanWindow"); 978 | if (!_sws_GetProgmanWindow) 979 | { 980 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_FUNCTION_NOT_FOUND), NULL); 981 | } 982 | } 983 | }*/ 984 | if (!rv) 985 | { 986 | if (!sws_InternalGetWindowText) 987 | { 988 | sws_InternalGetWindowText = GetProcAddress(_sws_hUser32, "InternalGetWindowText"); 989 | if (!sws_InternalGetWindowText) 990 | { 991 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_FUNCTION_NOT_FOUND), NULL); 992 | } 993 | } 994 | } 995 | if (!rv) 996 | { 997 | if (!_sws_InternalGetWindowIcon) 998 | { 999 | _sws_InternalGetWindowIcon = GetProcAddress(_sws_hUser32, "InternalGetWindowIcon"); 1000 | if (!_sws_InternalGetWindowIcon) 1001 | { 1002 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_FUNCTION_NOT_FOUND), NULL); 1003 | } 1004 | } 1005 | } 1006 | /*if (!rv) 1007 | { 1008 | if (!_sws_IsCoreWindow) 1009 | { 1010 | _sws_IsCoreWindow = GetProcAddress(_sws_hUser32, (LPCSTR)2572); 1011 | if (!_sws_IsCoreWindow) 1012 | { 1013 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_FUNCTION_NOT_FOUND), NULL); 1014 | } 1015 | } 1016 | }*/ 1017 | if (!rv) 1018 | { 1019 | if (!_sws_hUxtheme) 1020 | { 1021 | _sws_hUxtheme = LoadLibraryW(L"uxtheme.dll"); 1022 | if (!_sws_hUxtheme) 1023 | { 1024 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_LOADLIBRARY_FAILED), NULL); 1025 | } 1026 | } 1027 | } 1028 | if (!rv) 1029 | { 1030 | if (!_sws_ShouldSystemUseDarkMode) 1031 | { 1032 | _sws_ShouldSystemUseDarkMode = GetProcAddress(_sws_hUxtheme, (LPCSTR)138); 1033 | if (!_sws_ShouldSystemUseDarkMode) 1034 | { 1035 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_FUNCTION_NOT_FOUND), NULL); 1036 | } 1037 | } 1038 | } 1039 | if (!rv) 1040 | { 1041 | if (!_sws_SetPreferredAppMode) 1042 | { 1043 | _sws_SetPreferredAppMode = GetProcAddress(_sws_hUxtheme, (LPCSTR)135); 1044 | if (!_sws_SetPreferredAppMode) 1045 | { 1046 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_FUNCTION_NOT_FOUND), NULL); 1047 | } 1048 | } 1049 | } 1050 | if (!rv) 1051 | { 1052 | if (!_sws_AllowDarkModeForWindow) 1053 | { 1054 | _sws_AllowDarkModeForWindow = GetProcAddress(_sws_hUxtheme, (LPCSTR)133); 1055 | if (!_sws_AllowDarkModeForWindow) 1056 | { 1057 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_FUNCTION_NOT_FOUND), NULL); 1058 | } 1059 | } 1060 | } 1061 | if (!rv) 1062 | { 1063 | if (!_sws_RefreshImmersiveColorPolicyState) 1064 | { 1065 | _sws_RefreshImmersiveColorPolicyState = GetProcAddress(_sws_hUxtheme, (LPCSTR)104); 1066 | if (!_sws_RefreshImmersiveColorPolicyState) 1067 | { 1068 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_FUNCTION_NOT_FOUND), NULL); 1069 | } 1070 | } 1071 | } 1072 | if (!rv) 1073 | { 1074 | if (!_sws_hShcore) 1075 | { 1076 | _sws_hShcore = LoadLibraryW(L"shcore.dll"); 1077 | if (!_sws_hShcore) 1078 | { 1079 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_LOADLIBRARY_FAILED), NULL); 1080 | } 1081 | } 1082 | } 1083 | if (!rv) 1084 | { 1085 | if (!sws_SHWindowsPolicy) 1086 | { 1087 | sws_SHWindowsPolicy = GetProcAddress(_sws_hShcore, (LPCSTR)190); 1088 | if (!sws_SHWindowsPolicy) 1089 | { 1090 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_FUNCTION_NOT_FOUND), NULL); 1091 | } 1092 | } 1093 | } 1094 | if (!rv) 1095 | { 1096 | if (!sws_AppResolver) 1097 | { 1098 | CoCreateInstance(&CLSID_StartMenuCacheAndAppResolver, NULL, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, &IID_IAppResolver_8, (void**)&sws_AppResolver); 1099 | if (!sws_AppResolver) 1100 | { 1101 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_APPRESOLVER_NOT_AVAILABLE), NULL); 1102 | } 1103 | } 1104 | } 1105 | return rv; 1106 | } 1107 | -------------------------------------------------------------------------------- /SimpleWindowSwitcher/sws_WindowHelpers.h: -------------------------------------------------------------------------------- 1 | #ifndef _H_SWS_WINDOWHELPERS_H_ 2 | #define _H_SWS_WINDOWHELPERS_H_ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #pragma comment(lib, "Shlwapi.lib") 10 | #include 11 | #pragma comment(lib, "Dwmapi.lib") 12 | #include 13 | #include 14 | #pragma comment(lib, "Comctl32.lib") 15 | #pragma comment(lib, "Gdiplus.lib") 16 | #include 17 | #include 18 | #include 19 | #include "sws_def.h" 20 | #include "sws_error.h" 21 | #include "sws_tshwnd.h" 22 | 23 | extern ULONG_PTR _sws_gdiplus_token; 24 | extern RTL_OSVERSIONINFOW sws_global_rovi; 25 | extern DWORD32 sws_global_ubr; 26 | 27 | // References: 28 | // RealEnumWindows: https://stackoverflow.com/questions/38205375/enumwindows-function-in-win10-enumerates-only-desktop-apps 29 | // IsAltTabWindow: https://devblogs.microsoft.com/oldnewthing/20071008-00/?p=24863 30 | // GetIconFromHWND: https://github.com/cairoshell/ManagedShell/blob/master/src/ManagedShell.WindowsTasks/ApplicationWindow.cs 31 | 32 | // bcc18b79-ba16-442f-80c4-8a59c30c463b 33 | DEFINE_GUID(__uuidof_IShellItemImageFactory, 34 | 0xbcc18b79, 35 | 0xba16, 0x442f, 0x80, 0xc4, 36 | 0x8a, 0x59, 0xc3, 0x0c, 0x46, 0x3b 37 | ); 38 | 39 | DEFINE_GUID(__uuidof_IShellItem2, 40 | 0x7e9fb0d3, 41 | 0x919f, 0x4307, 0xab, 0x2e, 42 | 0x9b, 0x18, 0x60, 0x31, 0x0c, 0x93 43 | ); 44 | 45 | DEFINE_GUID(__uuidof_IPropertyStore, 46 | 0x886D8EEB, 47 | 0x8CF2, 0x4446, 0x8D, 0x02, 48 | 0xCD, 0xBA, 0x1D, 0xBD, 0xCF, 0x99 49 | ); 50 | 51 | DEFINE_GUID(__uuidof_AppUserModelIdProperty, 52 | 0x9F4C2855, 53 | 0x9F79, 0x4B39, 0xA8, 0xD0, 54 | 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3 55 | ); 56 | 57 | // https://gist.github.com/m417z/451dfc2dad88d7ba88ed1814779a26b4 58 | 59 | // {c8900b66-a973-584b-8cae-355b7f55341b} 60 | DEFINE_GUID(CLSID_StartMenuCacheAndAppResolver, 0x660b90c8, 0x73a9, 0x4b58, 0x8c, 0xae, 0x35, 0x5b, 0x7f, 0x55, 0x34, 0x1b); 61 | 62 | // {46a6eeff-908e-4dc6-92a6-64be9177b41c} 63 | DEFINE_GUID(IID_IAppResolver_7, 0x46a6eeff, 0x908e, 0x4dc6, 0x92, 0xa6, 0x64, 0xbe, 0x91, 0x77, 0xb4, 0x1c); 64 | 65 | // {de25675a-72de-44b4-9373-05170450c140} 66 | DEFINE_GUID(IID_IAppResolver_8, 0xde25675a, 0x72de, 0x44b4, 0x93, 0x73, 0x05, 0x17, 0x04, 0x50, 0xc1, 0x40); 67 | 68 | typedef interface IAppResolver_8 IAppResolver_8; 69 | 70 | typedef struct IAppResolver_8Vtbl 71 | { 72 | BEGIN_INTERFACE 73 | 74 | HRESULT(STDMETHODCALLTYPE* QueryInterface)( 75 | IAppResolver_8* This, 76 | /* [in] */ REFIID riid, 77 | /* [annotation][iid_is][out] */ 78 | _COM_Outptr_ void** ppvObject); 79 | 80 | ULONG(STDMETHODCALLTYPE* AddRef)( 81 | IAppResolver_8* This); 82 | 83 | ULONG(STDMETHODCALLTYPE* Release)( 84 | IAppResolver_8* This); 85 | 86 | HRESULT (STDMETHODCALLTYPE* GetAppIDForShortcut)(IAppResolver_8* This); 87 | HRESULT (STDMETHODCALLTYPE* GetAppIDForShortcutObject)(IAppResolver_8* This); 88 | HRESULT (STDMETHODCALLTYPE* GetAppIDForWindow)(IAppResolver_8* This, HWND hWnd, WCHAR** pszAppId, int* pUnknown1, int* pUnknown2, int* pUnknown3); 89 | HRESULT (STDMETHODCALLTYPE* GetAppIDForProcess)(IAppResolver_8* This, DWORD dwProcessId, WCHAR** pszAppId, int* pUnknown1, int* pUnknown2, int* pUnknown3); 90 | 91 | END_INTERFACE 92 | } IAppResolver_8Vtbl; 93 | 94 | interface IAppResolver_8 95 | { 96 | CONST_VTBL struct IAppResolver_8Vtbl* lpVtbl; 97 | }; 98 | 99 | #define STATUS_BUFFER_TOO_SMALL 0xC0000023 100 | typedef NTSTATUS(WINAPI* NtUserBuildHwndList) 101 | ( 102 | HDESK in_hDesk, 103 | HWND in_hWndNext, 104 | BOOL in_EnumChildren, 105 | BOOL in_RemoveImmersive, 106 | DWORD in_ThreadID, 107 | UINT in_Max, 108 | HWND* out_List, 109 | UINT* out_Cnt 110 | ); 111 | extern HMODULE _sws_hWin32u; 112 | extern NtUserBuildHwndList _sws_pNtUserBuildHwndList; 113 | 114 | typedef struct 115 | { 116 | int nAccentState; 117 | int nFlags; 118 | int nColor; 119 | int nAnimationId; 120 | } ACCENTPOLICY; 121 | typedef struct 122 | { 123 | int nAttribute; 124 | PVOID pData; 125 | ULONG ulDataSize; 126 | } WINCOMPATTRDATA; 127 | extern HINSTANCE _sws_hUser32; 128 | typedef BOOL(WINAPI* pSetWindowCompositionAttribute)(HWND, WINCOMPATTRDATA*); 129 | extern pSetWindowCompositionAttribute _sws_SetWindowCompositionAttribute; 130 | typedef BOOL(WINAPI* pIsShellManagedWindow)(HWND); 131 | extern pIsShellManagedWindow _sws_IsShellManagedWindow; 132 | extern pIsShellManagedWindow sws_IsShellFrameWindow; 133 | typedef HWND(WINAPI* pHungWindowFromGhostWindow)(HWND); 134 | extern pHungWindowFromGhostWindow _sws_HungWindowFromGhostWindow; 135 | typedef HWND(WINAPI* pGhostWindowFromHungWindow)(HWND); 136 | extern pGhostWindowFromHungWindow _sws_GhostWindowFromHungWindow; 137 | typedef HICON(WINAPI* pInternalGetWindowIcon)(HWND,UINT); 138 | extern pInternalGetWindowIcon _sws_InternalGetWindowIcon; 139 | typedef HWND(WINAPI* pIsCoreWindow)(HWND); 140 | extern pIsCoreWindow _sws_IsCoreWindow; 141 | 142 | typedef HWND(WINAPI* pCreateWindowInBand)( 143 | _In_ DWORD dwExStyle, 144 | _In_opt_ LPCWSTR lpClassName, 145 | _In_opt_ LPCWSTR lpWindowName, 146 | _In_ DWORD dwStyle, 147 | _In_ int X, 148 | _In_ int Y, 149 | _In_ int nWidth, 150 | _In_ int nHeight, 151 | _In_opt_ HWND hWndParent, 152 | _In_opt_ HMENU hMenu, 153 | _In_opt_ HINSTANCE hInstance, 154 | _In_opt_ LPVOID lpParam, 155 | DWORD band 156 | ); 157 | extern pCreateWindowInBand _sws_CreateWindowInBand; 158 | 159 | typedef BOOL(WINAPI* pSetWindowBand)(HWND hWnd, HWND hwndInsertAfter, DWORD dwBand); 160 | extern pSetWindowBand _sws_SetWindowBand; 161 | 162 | typedef BOOL(WINAPI* pGetWindowBand)(HWND hWnd, PDWORD pdwBand); 163 | extern pGetWindowBand _sws_GetWindowBand; 164 | 165 | extern FARPROC sws_SHRegGetValueFromHKCUHKLM; 166 | 167 | extern BOOL(*_sws_ShouldSystemUseDarkMode)(); 168 | extern void(*_sws_RefreshImmersiveColorPolicyState)(); 169 | extern HINSTANCE _sws_hUxtheme; 170 | 171 | BOOL(*_sws_IsTopLevelWindow)(HWND); 172 | 173 | HWND(*_sws_GetProgmanWindow)(); 174 | 175 | int(*sws_InternalGetWindowText)(HWND, LPWSTR, int); 176 | 177 | FILETIME sws_start_ft; 178 | FILETIME sws_ancient_ft; 179 | 180 | HICON sws_DefAppIcon; 181 | HICON sws_LegacyDefAppIcon; 182 | 183 | IAppResolver_8* sws_AppResolver; 184 | 185 | inline FILETIME sws_WindowHelpers_GetStartTime() 186 | { 187 | return sws_start_ft; 188 | } 189 | 190 | inline FILETIME sws_WindowHelpers_GetAncientTime() 191 | { 192 | ULARGE_INTEGER uli; 193 | uli.LowPart = sws_ancient_ft.dwLowDateTime; 194 | uli.HighPart = sws_ancient_ft.dwHighDateTime; 195 | uli.QuadPart--; 196 | sws_ancient_ft.dwHighDateTime = uli.HighPart; 197 | sws_ancient_ft.dwLowDateTime = uli.LowPart; 198 | return sws_ancient_ft; 199 | } 200 | 201 | enum ZBID 202 | { 203 | ZBID_DEFAULT = 0, 204 | ZBID_DESKTOP = 1, 205 | ZBID_UIACCESS = 2, 206 | ZBID_IMMERSIVE_IHM = 3, 207 | ZBID_IMMERSIVE_NOTIFICATION = 4, 208 | ZBID_IMMERSIVE_APPCHROME = 5, 209 | ZBID_IMMERSIVE_MOGO = 6, 210 | ZBID_IMMERSIVE_EDGY = 7, 211 | ZBID_IMMERSIVE_INACTIVEMOBODY = 8, 212 | ZBID_IMMERSIVE_INACTIVEDOCK = 9, 213 | ZBID_IMMERSIVE_ACTIVEMOBODY = 10, 214 | ZBID_IMMERSIVE_ACTIVEDOCK = 11, 215 | ZBID_IMMERSIVE_BACKGROUND = 12, 216 | ZBID_IMMERSIVE_SEARCH = 13, 217 | ZBID_GENUINE_WINDOWS = 14, 218 | ZBID_IMMERSIVE_RESTRICTED = 15, 219 | ZBID_SYSTEM_TOOLS = 16, 220 | ZBID_LOCK = 17, 221 | ZBID_ABOVELOCK_UX = 18, 222 | }; 223 | 224 | static BOOL _sws_IsBandValidToInclude[19] = { FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE }; 225 | 226 | HWND* _sws_WindowHelpers_Gui_BuildWindowList 227 | ( 228 | NtUserBuildHwndList pNtUserBuildHwndList, 229 | HDESK in_hDesk, 230 | HWND in_hWnd, 231 | BOOL in_EnumChildren, 232 | BOOL in_RemoveImmersive, 233 | UINT in_ThreadID, 234 | INT* out_Cnt 235 | ); 236 | 237 | /********************************************************/ 238 | /* enumerate all top level windows including metro apps */ 239 | /********************************************************/ 240 | sws_error_t sws_WindowHelpers_RealEnumWindows( 241 | WNDENUMPROC in_Proc, 242 | LPARAM in_Param 243 | ); 244 | 245 | wchar_t* sws_WindowHelpers_GetAUMIDForHWND(HWND hWnd); 246 | 247 | BOOL sws_WindowHelpers_IsValidMonitor(HMONITOR hMonitor, HDC unnamedParam2, LPRECT unnamedParam3, HMONITOR* pMonitor); 248 | 249 | sws_error_t sws_WindowHelpers_PermitDarkMode(HWND hWnd); 250 | 251 | sws_error_t sws_WindowHelpers_ShouldSystemUseDarkMode(DWORD* dwRes); 252 | 253 | sws_error_t sws_WindowHelpers_SetWindowBlur(HWND hWnd, int type, DWORD Color, DWORD Opacity); 254 | 255 | BOOL sws_WindowHelpers_IsTaskbarWindow(HWND hWnd, HWND hWndWallpaper); 256 | 257 | BOOL sws_WindowHelpers_IsAltTabWindow(HWND hwnd); 258 | 259 | void sws_WindowHelpers_GetDesktopText(wchar_t* wszTitle); 260 | 261 | BOOL sws_WindowHelpers_EnsureWallpaperHWND(); 262 | 263 | HWND sws_WindowHelpers_GetWallpaperHWND(); 264 | 265 | HBITMAP sws_WindowHelpers_CreateAlphaTextBitmap(LPCWSTR inText, HFONT inFont, DWORD dwTextFlags, SIZE size, COLORREF inColour); 266 | 267 | HWND sws_WindowHelpers_GetLastActivePopup(HWND hWnd); 268 | 269 | void sws_WindowHelpers_Clear(); 270 | 271 | sws_error_t sws_WindowHelpers_Initialize(); 272 | 273 | extern BOOL g_bIsDesktopRaised; 274 | inline BOOL _sws_WindowHelpers_IsDesktopRaised() 275 | { 276 | return g_bIsDesktopRaised; 277 | } 278 | 279 | inline void _sws_WindowHelpers_ToggleDesktop() 280 | { 281 | PostMessageW(FindWindowExW(NULL, NULL, L"Shell_TrayWnd", NULL), 0x579, 3 - _sws_WindowHelpers_IsDesktopRaised(), 0); 282 | //PostMessageW(FindWindowExW(NULL, NULL, L"Shell_TrayWnd", NULL), WM_HOTKEY, 513, 0); 283 | //PostMessageW(FindWindowExW(NULL, NULL, L"Shell_TrayWnd", NULL), 0x579, 3 - 1, 0); // 1 to restore 284 | //PostMessageW(FindWindowExW(NULL, NULL, L"Shell_TrayWnd", NULL), 0x579, 3 - 0, 0); // 0 to show 285 | } 286 | 287 | inline BOOL sws_WindowHelpers_IsWindowUWP(HWND hWnd) 288 | { 289 | return sws_IsShellFrameWindow(hWnd); 290 | return (GetPropW(hWnd, (LPCWSTR)0xA914)); 291 | } 292 | 293 | BOOL CALLBACK sws_WindowHelpers_AddAltTabWindowsToTimeStampedHWNDList(HWND hWnd, HDPA hdpa); 294 | 295 | BOOL sws_WindowHelpers_AreAnimationsAllowed(); 296 | 297 | void sws_WindowHelpers_GetWindowText(HWND hWnd, LPCWSTR lpWStr, DWORD dwLength); 298 | 299 | #define SWS_OSVERSION_INVALID 0xffffffff 300 | 301 | typedef LONG NTSTATUS, * PNTSTATUS; 302 | #define STATUS_SUCCESS (0x00000000) 303 | 304 | typedef NTSTATUS(WINAPI* VnRtlGetVersionPtr)(PRTL_OSVERSIONINFOW); 305 | 306 | // https://stackoverflow.com/questions/36543301/detecting-windows-10-version/36543774#36543774 307 | inline BOOL sws_WindowHelpers_GetOSVersion(PRTL_OSVERSIONINFOW lpRovi) 308 | { 309 | HMODULE hMod = GetModuleHandleW(L"ntdll.dll"); 310 | if (hMod != NULL) 311 | { 312 | VnRtlGetVersionPtr fxPtr = (VnRtlGetVersionPtr)GetProcAddress( 313 | hMod, 314 | "RtlGetVersion" 315 | ); 316 | if (fxPtr != NULL) 317 | { 318 | lpRovi->dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOW); 319 | if (STATUS_SUCCESS == fxPtr(lpRovi)) 320 | { 321 | return TRUE; 322 | } 323 | } 324 | } 325 | return FALSE; 326 | } 327 | 328 | // https://stackoverflow.com/questions/47926094/detecting-windows-10-os-build-minor-version 329 | inline DWORD32 sws_WindowHelpers_GetUBR() 330 | { 331 | DWORD32 ubr = 0, ubr_size = sizeof(DWORD32); 332 | HKEY hKey; 333 | LONG lRes = RegOpenKeyExW( 334 | HKEY_LOCAL_MACHINE, 335 | wcschr( 336 | wcschr( 337 | wcschr( 338 | UNIFIEDBUILDREVISION_KEY, 339 | '\\' 340 | ) + 1, 341 | '\\' 342 | ) + 1, 343 | '\\' 344 | ) + 1, 345 | 0, 346 | KEY_READ, 347 | &hKey 348 | ); 349 | if (lRes == ERROR_SUCCESS) 350 | { 351 | RegQueryValueExW( 352 | hKey, 353 | UNIFIEDBUILDREVISION_VALUE, 354 | 0, 355 | NULL, 356 | &ubr, 357 | &ubr_size 358 | ); 359 | } 360 | } 361 | 362 | inline DWORD32 sws_WindowHelpers_GetOSVersionAndUBR(PRTL_OSVERSIONINFOW lpRovi) 363 | { 364 | if (!sws_WindowHelpers_GetOSVersion(lpRovi)) 365 | { 366 | return SWS_OSVERSION_INVALID; 367 | } 368 | return sws_WindowHelpers_GetUBR(); 369 | } 370 | 371 | inline BOOL sws_WindowHelpers_IsWindows11() 372 | { 373 | if (!sws_global_rovi.dwMajorVersion) sws_global_ubr = sws_WindowHelpers_GetOSVersionAndUBR(&sws_global_rovi); 374 | if (sws_global_rovi.dwBuildNumber >= 21996) return TRUE; 375 | return FALSE; 376 | } 377 | 378 | inline HRESULT sws_WindowHelpers_SetMicaMaterialForThisWindow(HWND hWnd, BOOL bApply) 379 | { 380 | if (!sws_WindowHelpers_IsWindows11()) return S_FALSE; 381 | DWORD dwAttribute = (sws_global_rovi.dwBuildNumber >= 22523) ? 38 : DWMWA_MICA_EFFFECT; 382 | DWORD dwProp = (bApply ? ((sws_global_rovi.dwBuildNumber >= 22523) ? 2 : 1) : 0); 383 | return DwmSetWindowAttribute(hWnd, dwAttribute, &dwProp, sizeof(DWORD)); 384 | } 385 | #endif -------------------------------------------------------------------------------- /SimpleWindowSwitcher/sws_WindowSwitcher.h: -------------------------------------------------------------------------------- 1 | #ifndef _H_SWS_WINDOWSWITCHER_H_ 2 | #define _H_SWS_WINDOWSWITCHER_H_ 3 | #define _CRTDBG_MAP_ALLOC 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #pragma comment(lib, "Uxtheme.lib") 12 | #include 13 | #pragma comment(lib, "Shlwapi.lib") 14 | #include 15 | #pragma comment(lib, "runtimeobject.lib") 16 | #include 17 | #include "sws_def.h" 18 | #include "sws_error.h" 19 | #include "sws_window.h" 20 | #include "sws_utility.h" 21 | #include "sws_WindowSwitcherLayout.h" 22 | #include "sws_RegistryMonitor.h" 23 | #include "sws_tshwnd.h" 24 | 25 | typedef struct _sws_WindowSwitcher 26 | { 27 | BOOL bIsDynamic; 28 | BOOL bWithRegMon; 29 | HRESULT hrCo; 30 | HRESULT hrRo; 31 | HMODULE hinstDLL; 32 | HWND hWnd; 33 | UINT msgShellHook; 34 | sws_WindowSwitcherLayout layout; 35 | //sws_WindowSwitcherLayout layouts[SWS_WINDOWSWITCHER_MAX_NUM_MONITORS]; 36 | //sws_WindowSwitcherLayout minilayouts[SWS_WINDOWSWITCHER_MAX_NUM_MONITORS]; 37 | //UINT numLayouts; 38 | //UINT numLayoutsMax; 39 | HBRUSH hContourBrush; 40 | HBRUSH hBackgroundBrush; 41 | HBRUSH hCloseButtonBrush; 42 | HTHEME hTheme; 43 | BOOL bPartialRedraw; 44 | ITaskbarList* pTaskList; 45 | HWND hWndLast; 46 | BOOL bWasControl; 47 | sws_RegistryMonitor rm; 48 | HMONITOR hMonitor; 49 | INT cwIndex; 50 | DWORD cwMask; 51 | HANDLE hEvExit; 52 | BOOL bIsDarkMode; 53 | BOOL bIsMouseClicking; 54 | long long last_change; 55 | sws_vector pHWNDList; 56 | HDPA htshwnds; 57 | BOOL bWallpaperAlwaysLast; 58 | HWND hWndWallpaper; 59 | UINT mode; 60 | HWND lastMiniModehWnd; 61 | HWINEVENTHOOK global_hook; 62 | DWORD dwColorScheme; 63 | DWORD dwTheme; 64 | DWORD dwCornerPreference; 65 | DWORD dwShowDelay; 66 | BOOL bPrimaryOnly; 67 | sws_IInputSwitchCallback InputSwitchCallback; 68 | sws_IInputSwitchControl* pInputSwitchControl; 69 | UINT vkTilde; 70 | UINT dwOverrideMode; 71 | UINT opacity; 72 | HANDLE hShowThread; 73 | HANDLE hShowSignal; 74 | BOOL bNoPerApplicationListPrevious; 75 | BOOL bIsInitialized; 76 | HWND hWndAccessible; 77 | IAccPropServices* pAccPropServices; 78 | HBRUSH hFlashBrush; 79 | DWORD dwPaintFlags; 80 | HANDLE hFlashAnimationThread; 81 | HANDLE hFlashAnimationSignal; 82 | HDC hdcWindow; 83 | HDC hdcPaint; 84 | HPAINTBUFFER hBufferedPaint; 85 | INT cwOldIndex; 86 | DWORD cwOldMask; 87 | HDPA hLastClosedWnds; 88 | long long lastUpdateTime; 89 | BOOL bShouldStartFlashTimerWhenShowing; 90 | DWORD dwOriginalMouseRouting; 91 | DWORD dwOriginalScrollWheelBehavior; 92 | 93 | DWORD dwRowHeight; 94 | DWORD dwMaxWP; 95 | DWORD dwMaxHP; 96 | DWORD bIncludeWallpaper; 97 | DWORD bPerMonitor; 98 | DWORD dwMaxAbsoluteWP; 99 | DWORD dwMaxAbsoluteHP; 100 | DWORD bNoPerApplicationList; 101 | DWORD dwMasterPadding; 102 | DWORD dwWallpaperSupport; 103 | DWORD bSwitcherIsPerApplication; 104 | DWORD bAlwaysUseWindowTitleAndIcon; 105 | DWORD dwScrollWheelBehavior; 106 | DWORD bScrollWheelInvert; 107 | } sws_WindowSwitcher; 108 | 109 | typedef struct _sws_WindowSwitcher_EndTaskThreadParams 110 | { 111 | HWND hWnd; 112 | sws_WindowSwitcher* sws; 113 | HDESK hDesktop; 114 | } sws_WindowSwitcher_EndTaskThreadParams; 115 | 116 | static void _sws_WindowSwitcher_UpdateAccessibleText(sws_WindowSwitcher* _this); 117 | 118 | sws_error_t sws_WindowSwitcher_RegisterHotkeys(sws_WindowSwitcher* _this, HKL hkl); 119 | 120 | void sws_WindowSwitcher_UnregisterHotkeys(sws_WindowSwitcher* _this); 121 | 122 | void sws_WindowSwitcher_RefreshTheme(sws_WindowSwitcher* _this); 123 | 124 | void _sws_WindowSwitcher_SwitchToSelectedItemAndDismiss(sws_WindowSwitcher* _this); 125 | 126 | static void WINAPI _sws_WindowSwitcher_Calculate(sws_WindowSwitcher* _this, sws_WindowSwitcherLayout* pOldLayout); 127 | 128 | static sws_error_t _sws_WindowSwitcher_GetCloseButtonRectFromIndex(sws_WindowSwitcher* _this, DWORD dwIndex, LPRECT lpRect); 129 | 130 | static LRESULT _sws_WindowsSwitcher_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 131 | 132 | static sws_error_t _sws_WindowSwitcher_RegisterWindowClass(sws_WindowSwitcher* _this); 133 | 134 | __declspec(dllexport) sws_error_t sws_WindowSwitcher_RunMessageQueue(sws_WindowSwitcher* _this); 135 | 136 | __declspec(dllexport) void sws_WindowSwitcher_Clear(sws_WindowSwitcher* _this); 137 | 138 | __declspec(dllexport) sws_error_t sws_WindowSwitcher_Initialize(sws_WindowSwitcher** __this, BOOL bWithRegMon); 139 | 140 | void sws_WindowSwitcher_Paint(sws_WindowSwitcher* _this, DWORD dwFlags); 141 | 142 | void sws_WindowSwitcher_InitializeDefaultSettings(sws_WindowSwitcher* _this); 143 | 144 | #endif 145 | -------------------------------------------------------------------------------- /SimpleWindowSwitcher/sws_WindowSwitcherLayout.c: -------------------------------------------------------------------------------- 1 | #include "sws_WindowSwitcherLayout.h" 2 | 3 | static BOOL CALLBACK _sws_WindowSwitcherLayout_EnumWindowsCallback(_In_ HWND hWnd, _In_ sws_WindowSwitcherLayout* _this) 4 | { 5 | if (sws_WindowHelpers_IsAltTabWindow(hWnd) || (_this->bIncludeWallpaper && !_this->bWallpaperAlwaysLast && hWnd == _this->hWndWallpaper)) 6 | { 7 | sws_WindowSwitcherLayoutWindow swsLayoutWindow; 8 | sws_WindowSwitcherLayoutWindow_Initialize(&swsLayoutWindow, hWnd, NULL); 9 | sws_vector_PushBack(&_this->pWindowList, &swsLayoutWindow); 10 | DWORD band; 11 | _sws_GetWindowBand(hWnd, &band); 12 | if (band != ZBID_DESKTOP) _this->numTopMost++; 13 | } 14 | 15 | return TRUE; 16 | } 17 | 18 | sws_error_t sws_WindowSwitcherLayout_InvalidateLayout(sws_WindowSwitcherLayout* _this) 19 | { 20 | sws_error_t rv = SWS_ERROR_SUCCESS; 21 | 22 | sws_WindowSwitcherLayoutWindow* pWindowList = _this->pWindowList.pList; 23 | for (int iCurrentWindow = _this->pWindowList.cbSize - 1; iCurrentWindow >= 0; iCurrentWindow--) 24 | { 25 | sws_WindowSwitcherLayoutWindow_Erase(&(pWindowList[iCurrentWindow])); 26 | } 27 | 28 | return rv; 29 | } 30 | 31 | static inline unsigned int _sws_WindowSwitcherLayout_GetInitialLeft(sws_WindowSwitcherLayout* _this) 32 | { 33 | return _this->cbElementLeftPadding + _this->cbLeftPadding; 34 | } 35 | 36 | static inline unsigned int _sws_WindowSwitcherLayout_GetRightIncrement(sws_WindowSwitcherLayout* _this) 37 | { 38 | return (_this->cbRightPadding + _this->cbElementRightPadding) + _sws_WindowSwitcherLayout_GetInitialLeft(_this); 39 | } 40 | 41 | static inline unsigned int _sws_WindowSwitcherLayout_GetInitialTop(sws_WindowSwitcherLayout* _this) 42 | { 43 | return _this->cbElementTopPadding + (_this->cbTopPadding + _this->cbRowTitleHeight + _this->cbVDividerPadding); 44 | } 45 | 46 | static inline unsigned int _sws_WindowSwitcherLayout_GetBottomIncrement(sws_WindowSwitcherLayout* _this) 47 | { 48 | return (_this->cbThumbnailAvailableHeight + _this->cbBottomPadding) + _this->cbElementBottomPadding + _sws_WindowSwitcherLayout_GetInitialTop(_this); 49 | } 50 | 51 | sws_error_t sws_WindowSwitcherLayout_ComputeLayout(sws_WindowSwitcherLayout* _this, int direction, HWND hTarget) 52 | { 53 | sws_error_t rv = SWS_ERROR_SUCCESS; 54 | 55 | if (!rv) 56 | { 57 | unsigned int cbInitialLeft = _sws_WindowSwitcherLayout_GetInitialLeft(_this) + _this->cbMasterLeftPadding; 58 | unsigned int cbInitialTop = _sws_WindowSwitcherLayout_GetInitialTop(_this) + _this->cbMasterTopPadding; 59 | unsigned int cbCurrentLeft = cbInitialLeft, cbCurrentTop = cbInitialTop; 60 | unsigned int cbMaxWidthHit = 0, cbMaxWidth = 0; 61 | 62 | int iObtainedIndex = 0; 63 | 64 | sws_WindowSwitcherLayoutWindow* pWindowList = _this->pWindowList.pList; 65 | 66 | if (_this->iWidth) 67 | { 68 | cbMaxWidth = _this->iWidth; // -_this->cbPadding; !!!!! 69 | } 70 | else 71 | { 72 | cbMaxWidth = _this->cbMaxWidth; 73 | } 74 | 75 | BOOL bHasTarget = FALSE; 76 | int iTarget = -1; 77 | 78 | if (direction != SWS_WINDOWSWITCHERLAYOUT_COMPUTE_DIRECTION_INITIAL) 79 | { 80 | if (direction == SWS_WINDOWSWITCHERLAYOUT_COMPUTE_DIRECTION_BACKWARD) 81 | { 82 | bHasTarget = TRUE; 83 | iObtainedIndex = _this->pWindowList.cbSize - 1; 84 | } 85 | else if (direction == SWS_WINDOWSWITCHERLAYOUT_COMPUTE_DIRECTION_FORWARD) 86 | { 87 | if (_this->iIndex == _this->pWindowList.cbSize - 1) 88 | { 89 | iObtainedIndex = _this->iIndex; 90 | } 91 | else 92 | { 93 | int i = 0, state = 0, h = 0; 94 | for (i = _this->pWindowList.cbSize - 1; i >= 0; i--) 95 | { 96 | if (state == 0) 97 | { 98 | if (pWindowList[i].hThumbnail) 99 | { 100 | h = pWindowList[i].rcThumbnail.top; 101 | state = 1; 102 | } 103 | } 104 | else if (state == 1) 105 | { 106 | if (h != pWindowList[i].rcThumbnail.top) 107 | { 108 | break; 109 | } 110 | } 111 | } 112 | iObtainedIndex = i; 113 | } 114 | } 115 | sws_WindowSwitcherLayout_InvalidateLayout(_this); 116 | } 117 | 118 | BOOL bIsWidthComputed = (_this->iWidth != 0); 119 | BOOL bFinishedLayout = FALSE; 120 | 121 | while (1) 122 | { 123 | int iCurrentCount = 0; 124 | 125 | cbInitialLeft = _sws_WindowSwitcherLayout_GetInitialLeft(_this) + _this->cbMasterLeftPadding; 126 | cbInitialTop = _sws_WindowSwitcherLayout_GetInitialTop(_this) + _this->cbMasterTopPadding; 127 | cbCurrentLeft = cbInitialLeft; 128 | cbCurrentTop = cbInitialTop; 129 | //cbMaxWidthHit = 0, cbMaxWidth = 0; // remove this so the maximum width hit is preserved across hwnd searches or backward iterations 130 | 131 | for (int iCurrentWindow = iObtainedIndex ? iObtainedIndex : _this->iIndex; iCurrentWindow >= 0; iCurrentWindow--) 132 | { 133 | //TCHAR name[200]; 134 | //GetWindowTextW(pWindowList[iCurrentWindow].hWnd, name, 200); 135 | //wprintf(L"%d %s ", pWindowList[iCurrentWindow].hWnd, name); 136 | 137 | if (pWindowList[iCurrentWindow].hWnd == _this->hWnd) 138 | { 139 | continue; 140 | } 141 | 142 | if (_this->iWidth) 143 | { 144 | cbMaxWidth = _this->iWidth; // +_this->cbPadding + _this->cbRightPadding; // ????? 145 | } 146 | else 147 | { 148 | cbMaxWidth = _this->cbMaxWidth; 149 | } 150 | 151 | if (!bFinishedLayout) 152 | { 153 | pWindowList[iCurrentWindow].iRowMax = -1; 154 | } 155 | 156 | DwmRegisterThumbnail( 157 | _this->hWnd, 158 | pWindowList[iCurrentWindow].hWnd, 159 | &(pWindowList[iCurrentWindow].hThumbnail) 160 | ); 161 | DwmQueryThumbnailSourceSize(pWindowList[iCurrentWindow].hThumbnail, &(pWindowList[iCurrentWindow].sizWindow)); 162 | if ((pWindowList[iCurrentWindow].sizWindow.cy == 0) || 163 | (pWindowList[iCurrentWindow].sizWindow.cx == 0)) 164 | { 165 | // Fix for windows with no height or width not being displayed. 166 | pWindowList[iCurrentWindow].dwWindowFlags |= SWS_WINDOWSWITCHERLAYOUT_WINDOWFLAGS_ISEMPTY; 167 | pWindowList[iCurrentWindow].sizWindow.cy = SWS_WINDOWSWITCHERLAYOUT_EMPTYWINDOW_THUMBNAIL_HEIGHT * (_this->cbDpiY / DEFAULT_DPI_Y); 168 | pWindowList[iCurrentWindow].sizWindow.cx = SWS_WINDOWSWITCHERLAYOUT_EMPTYWINDOW_THUMBNAIL_WIDTH * (_this->cbDpiX / DEFAULT_DPI_X); 169 | } 170 | if (pWindowList[iCurrentWindow].sizWindow.cy < SWS_WINDOWSWITCHERLAYOUT_THUMBNAIL_MINHEIGHT * (_this->cbDpiY / DEFAULT_DPI_Y)) 171 | { 172 | pWindowList[iCurrentWindow].dwWindowFlags |= SWS_WINDOWSWITCHERLAYOUT_WINDOWFLAGS_ISTOOSMALLVERTICAL; 173 | pWindowList[iCurrentWindow].sizWindow.cy = SWS_WINDOWSWITCHERLAYOUT_THUMBNAIL_MINHEIGHT * (_this->cbDpiY / DEFAULT_DPI_Y); 174 | } 175 | if (pWindowList[iCurrentWindow].sizWindow.cx < SWS_WINDOWSWITCHERLAYOUT_THUMBNAIL_MINWIDTH * (_this->cbDpiX / DEFAULT_DPI_X)) 176 | { 177 | pWindowList[iCurrentWindow].dwWindowFlags |= SWS_WINDOWSWITCHERLAYOUT_WINDOWFLAGS_ISTOOSMALLHORIZONTAL; 178 | pWindowList[iCurrentWindow].sizWindow.cx = SWS_WINDOWSWITCHERLAYOUT_THUMBNAIL_MINWIDTH * (_this->cbDpiX / DEFAULT_DPI_X); 179 | } 180 | if (bFinishedLayout) 181 | { 182 | DwmUnregisterThumbnail(pWindowList[iCurrentWindow].hThumbnail); 183 | pWindowList[iCurrentWindow].hThumbnail = 0; 184 | } 185 | 186 | unsigned int width = 0, original_width = 0; 187 | // original_width indicates that the window's width was too large, so we 188 | // reduced it and need to scale the height appropiately 189 | 190 | if (_this->bIncludeWallpaper && pWindowList[iCurrentWindow].hWnd == _this->hWndWallpaper) 191 | { 192 | width = ((_this->mi.rcMonitor.right - _this->mi.rcMonitor.left) * 193 | _this->cbThumbnailAvailableHeight) / 194 | (_this->mi.rcMonitor.bottom - _this->mi.rcMonitor.top); 195 | } 196 | else 197 | { 198 | width = ((pWindowList[iCurrentWindow].sizWindow.cx) * 199 | _this->cbThumbnailAvailableHeight) / 200 | (pWindowList[iCurrentWindow].sizWindow.cy); 201 | } 202 | if (width > _this->cbMaxTileWidth || width > pWindowList[iCurrentWindow].sizWindow.cx) 203 | { 204 | original_width = width; 205 | if (width > _this->cbMaxTileWidth) 206 | { 207 | width = _this->cbMaxTileWidth; 208 | } 209 | if (width > pWindowList[iCurrentWindow].sizWindow.cx) 210 | { 211 | width = pWindowList[iCurrentWindow].sizWindow.cx; 212 | } 213 | } 214 | 215 | if (bFinishedLayout) 216 | { 217 | pWindowList[iCurrentWindow].sizWindow.cx = 0; 218 | pWindowList[iCurrentWindow].sizWindow.cy = 0; 219 | } 220 | 221 | //wchar_t name[200]; 222 | //GetWindowTextW(pWindowList[iCurrentWindow].hWnd, name, 200); 223 | //wprintf(L"%s %d %f %d\n", name, cbCurrentLeft, width, cbMaxWidth); 224 | 225 | BOOL bPreventSingleItemOnLastRow = FALSE; 226 | if (iCurrentWindow == 1) 227 | { 228 | HTHUMBNAIL hThumbnail = NULL; 229 | DwmRegisterThumbnail( 230 | _this->hWnd, 231 | pWindowList[0].hWnd, 232 | &hThumbnail 233 | ); 234 | SIZE sz; 235 | sz.cx = 0; 236 | sz.cy = 0; 237 | if (hThumbnail) 238 | { 239 | DwmQueryThumbnailSourceSize(hThumbnail, &sz); 240 | DwmUnregisterThumbnail(hThumbnail); 241 | } 242 | if ((sz.cy == 0) || (sz.cx == 0)) 243 | { 244 | sz.cy = SWS_WINDOWSWITCHERLAYOUT_EMPTYWINDOW_THUMBNAIL_HEIGHT * (_this->cbDpiY / DEFAULT_DPI_Y); 245 | sz.cx = SWS_WINDOWSWITCHERLAYOUT_EMPTYWINDOW_THUMBNAIL_WIDTH * (_this->cbDpiX / DEFAULT_DPI_X); 246 | } 247 | if (sz.cy < SWS_WINDOWSWITCHERLAYOUT_THUMBNAIL_MINHEIGHT * (_this->cbDpiY / DEFAULT_DPI_Y)) 248 | { 249 | sz.cy = SWS_WINDOWSWITCHERLAYOUT_THUMBNAIL_MINHEIGHT * (_this->cbDpiY / DEFAULT_DPI_Y); 250 | } 251 | if (sz.cx < SWS_WINDOWSWITCHERLAYOUT_THUMBNAIL_MINWIDTH * (_this->cbDpiX / DEFAULT_DPI_X)) 252 | { 253 | sz.cx = SWS_WINDOWSWITCHERLAYOUT_THUMBNAIL_MINWIDTH * (_this->cbDpiX / DEFAULT_DPI_X); 254 | } 255 | unsigned int next_width = 0; 256 | if (_this->bIncludeWallpaper && pWindowList[0].hWnd == _this->hWndWallpaper) 257 | { 258 | next_width = ((_this->mi.rcMonitor.right - _this->mi.rcMonitor.left) * 259 | _this->cbThumbnailAvailableHeight) / 260 | (_this->mi.rcMonitor.bottom - _this->mi.rcMonitor.top); 261 | } 262 | else 263 | { 264 | next_width = ((sz.cx) * 265 | _this->cbThumbnailAvailableHeight) / 266 | (sz.cy); 267 | } 268 | if (next_width > _this->cbMaxTileWidth || next_width > sz.cx) 269 | { 270 | if (next_width > _this->cbMaxTileWidth) 271 | { 272 | next_width = _this->cbMaxTileWidth; 273 | } 274 | if (next_width > sz.cx) 275 | { 276 | next_width = sz.cx; 277 | } 278 | } 279 | bPreventSingleItemOnLastRow = (cbCurrentLeft + width + next_width + _sws_WindowSwitcherLayout_GetRightIncrement(_this) * 2 - _sws_WindowSwitcherLayout_GetInitialLeft(_this)) > (cbMaxWidth - _this->cbMasterRightPadding); 280 | } 281 | 282 | //if (cbCurrentLeft + width + _this->cbRightPadding + _this->cbPadding > cbMaxWidth) !!!!! 283 | if (bPreventSingleItemOnLastRow || 284 | (cbCurrentLeft + width + _sws_WindowSwitcherLayout_GetRightIncrement(_this) - _sws_WindowSwitcherLayout_GetInitialLeft(_this) > cbMaxWidth - _this->cbMasterRightPadding)) 285 | { 286 | if (!bFinishedLayout) 287 | { 288 | int t = iCurrentWindow + 1; 289 | if (t == _this->pWindowList.cbSize) t = 0; 290 | for (int k = t; k < _this->pWindowList.cbSize; ++k) 291 | { 292 | if (pWindowList[k].iRowMax == -1) 293 | { 294 | pWindowList[k].iRowMax = cbCurrentLeft - _sws_WindowSwitcherLayout_GetInitialLeft(_this); 295 | } 296 | else 297 | { 298 | break; 299 | } 300 | //if (k == _this->pWindowList.cbSize - 1) 301 | //{ 302 | // k = -1; 303 | //} 304 | } 305 | } 306 | //if (cbCurrentLeft - _this->cbLeftPadding > cbMaxWidthHit) !!!!! 307 | if (cbCurrentLeft - _sws_WindowSwitcherLayout_GetInitialLeft(_this) > cbMaxWidthHit) 308 | { 309 | // cbMaxWidthHit = cbCurrentLeft - _this->cbLeftPadding; !!!!! 310 | cbMaxWidthHit = cbCurrentLeft - _sws_WindowSwitcherLayout_GetInitialLeft(_this); 311 | } 312 | if (!bFinishedLayout) 313 | { 314 | //printf( 315 | // "compare %d %f %f\n", iCurrentWindow, 316 | // cbCurrentTop + _this->cbThumbnailAvailableHeight + _this->cbBottomPadding, 317 | // _this->cbMaxHeight 318 | // ); 319 | 320 | //if (cbCurrentTop + _this->cbThumbnailAvailableHeight + _this->cbBottomPadding > _this->cbMaxHeight) !!!!! 321 | if (cbCurrentTop + 322 | _sws_WindowSwitcherLayout_GetBottomIncrement(_this) + 323 | _sws_WindowSwitcherLayout_GetBottomIncrement(_this) - _sws_WindowSwitcherLayout_GetInitialTop(_this) > 324 | _this->cbMaxHeight - _this->cbMasterBottomPadding) 325 | { 326 | //cbCurrentTop -= cbInitialTop + _this->cbPadding; !!! 327 | 328 | HWND hWnd = pWindowList[iCurrentWindow].hWnd; 329 | sws_WindowSwitcherLayoutWindow_Erase(&(pWindowList[iCurrentWindow])); 330 | iTarget = iCurrentWindow + 1; 331 | 332 | if (bIsWidthComputed) 333 | { 334 | break; 335 | } 336 | else 337 | { 338 | bFinishedLayout = TRUE; 339 | } 340 | } 341 | } 342 | if (!bFinishedLayout) 343 | { 344 | //cbCurrentTop += _this->cbThumbnailAvailableHeight + _this->cbBottomPadding + cbInitialTop; !!!!! 345 | cbCurrentTop = cbCurrentTop + _sws_WindowSwitcherLayout_GetBottomIncrement(_this); 346 | } 347 | cbCurrentLeft = cbInitialLeft; 348 | } 349 | 350 | //printf("%d %d\n", cbCurrentLeft, cbCurrentTop); 351 | 352 | if (!bFinishedLayout) 353 | { 354 | long hdiff = 0; 355 | 356 | pWindowList[iCurrentWindow].rcThumbnail.left = cbCurrentLeft; 357 | pWindowList[iCurrentWindow].rcThumbnail.top = cbCurrentTop; 358 | pWindowList[iCurrentWindow].rcThumbnail.right = cbCurrentLeft + width; 359 | if (pWindowList[iCurrentWindow].dwWindowFlags & SWS_WINDOWSWITCHERLAYOUT_WINDOWFLAGS_ISTOOSMALLHORIZONTAL) 360 | { 361 | SIZE szTemp; 362 | DwmQueryThumbnailSourceSize(pWindowList[iCurrentWindow].hThumbnail, &szTemp); 363 | pWindowList[iCurrentWindow].rcThumbnail.right = cbCurrentLeft + szTemp.cx; 364 | } 365 | pWindowList[iCurrentWindow].rcThumbnail.bottom = cbCurrentTop + _this->cbThumbnailAvailableHeight; 366 | if (original_width) 367 | { 368 | hdiff = pWindowList[iCurrentWindow].rcThumbnail.bottom; 369 | pWindowList[iCurrentWindow].rcThumbnail.bottom = cbCurrentTop + (width * _this->cbThumbnailAvailableHeight) / (original_width * 1.0); 370 | hdiff -= pWindowList[iCurrentWindow].rcThumbnail.bottom; 371 | } 372 | if (pWindowList[iCurrentWindow].dwWindowFlags & SWS_WINDOWSWITCHERLAYOUT_WINDOWFLAGS_ISTOOSMALLVERTICAL) 373 | { 374 | SIZE szTemp; 375 | DwmQueryThumbnailSourceSize(pWindowList[iCurrentWindow].hThumbnail, &szTemp); 376 | pWindowList[iCurrentWindow].rcThumbnail.right = cbCurrentLeft + szTemp.cy; 377 | } 378 | 379 | pWindowList[iCurrentWindow].rcWindow.left = cbCurrentLeft - _sws_WindowSwitcherLayout_GetInitialLeft(_this) + _this->cbElementLeftPadding; 380 | pWindowList[iCurrentWindow].rcWindow.top = cbCurrentTop - _sws_WindowSwitcherLayout_GetInitialTop(_this) + _this->cbElementTopPadding; 381 | pWindowList[iCurrentWindow].rcWindow.right = cbCurrentLeft + width + _sws_WindowSwitcherLayout_GetRightIncrement(_this) - _sws_WindowSwitcherLayout_GetInitialLeft(_this) - _this->cbElementRightPadding; 382 | pWindowList[iCurrentWindow].rcWindow.bottom = cbCurrentTop + _sws_WindowSwitcherLayout_GetBottomIncrement(_this) - _sws_WindowSwitcherLayout_GetInitialTop(_this) - _this->cbElementBottomPadding; 383 | if (original_width) 384 | { 385 | pWindowList[iCurrentWindow].rcWindow.bottom -= hdiff; 386 | } 387 | 388 | //pWindowList[iCurrentWindow].rcWindow.left = cbCurrentLeft - _this->cbLeftPadding; 389 | //pWindowList[iCurrentWindow].rcWindow.top = cbCurrentTop - _this->cbTopPadding - _this->cbRowTitleHeight; 390 | //pWindowList[iCurrentWindow].rcWindow.right = cbCurrentLeft + width + _this->cbRightPadding; 391 | //pWindowList[iCurrentWindow].rcWindow.bottom = cbCurrentTop + _this->cbThumbnailAvailableHeight + _this->cbBottomPadding; 392 | } 393 | 394 | //cbCurrentLeft += width + _this->cbRightPadding + _this->cbLeftPadding; !!!!! 395 | cbCurrentLeft = cbCurrentLeft + width + _sws_WindowSwitcherLayout_GetRightIncrement(_this); 396 | 397 | /*if (iCurrentWindow == 0) 398 | { 399 | iCurrentWindow = _this->pWindowList.cbSize; 400 | }*/ 401 | 402 | iTarget = iCurrentWindow; 403 | iCurrentCount++; 404 | if (iCurrentCount == _this->pWindowList.cbSize) 405 | { 406 | break; 407 | } 408 | } 409 | 410 | if (hTarget && direction == SWS_WINDOWSWITCHERLAYOUT_COMPUTE_DIRECTION_INITIAL) 411 | { 412 | BOOL bShouldBreak = FALSE; 413 | int iObtained = 0; 414 | for (int j = 0; j < _this->pWindowList.cbSize; ++j) 415 | { 416 | if (pWindowList[j].hThumbnail) 417 | { 418 | if (pWindowList[j].hWnd == hTarget) 419 | { 420 | bShouldBreak = TRUE; 421 | } 422 | iObtained = j; 423 | } 424 | } 425 | if (bShouldBreak) 426 | { 427 | break; 428 | } 429 | else 430 | { 431 | int iTmpTop = pWindowList[iObtained].rcWindow.top; 432 | for (int j = iObtained; j >= 0; j--) 433 | { 434 | if (pWindowList[j].rcWindow.top != iTmpTop) 435 | { 436 | iObtained = j; 437 | break; 438 | } 439 | } 440 | sws_WindowSwitcherLayout_InvalidateLayout(_this); 441 | iObtainedIndex = iObtained; 442 | bFinishedLayout = FALSE; 443 | } 444 | continue; 445 | } 446 | if (!bHasTarget) 447 | { 448 | break; 449 | } 450 | else 451 | { 452 | int j, h = pWindowList[iObtainedIndex].rcThumbnail.top; 453 | if (!iTarget) 454 | { 455 | break; 456 | } 457 | for (j = iObtainedIndex; j >= 0; j--) 458 | { 459 | if (pWindowList[j].rcThumbnail.top != h) 460 | { 461 | j++; 462 | break; 463 | } 464 | } 465 | if (j == _this->iIndex) 466 | { 467 | break; 468 | } 469 | else 470 | { 471 | sws_WindowSwitcherLayout_InvalidateLayout(_this); 472 | iObtainedIndex = j - 1; 473 | } 474 | } 475 | } 476 | 477 | //if (cbCurrentLeft - _this->cbLeftPadding > cbMaxWidthHit) !!!!! 478 | if (cbCurrentLeft - _sws_WindowSwitcherLayout_GetInitialLeft(_this) > cbMaxWidthHit) 479 | { 480 | // cbMaxWidthHit = cbCurrentLeft - _this->cbLeftPadding; !!!!! 481 | cbMaxWidthHit = cbCurrentLeft - _sws_WindowSwitcherLayout_GetInitialLeft(_this); 482 | } 483 | for (int k = 0; k < _this->pWindowList.cbSize; ++k) 484 | { 485 | if (pWindowList[k].iRowMax == -1) 486 | { 487 | pWindowList[k].iRowMax = cbCurrentLeft - _sws_WindowSwitcherLayout_GetInitialLeft(_this); 488 | } 489 | else 490 | { 491 | break; 492 | } 493 | //if (k == _this->pWindowList.cbSize - 1) 494 | //{ 495 | // k = -1; 496 | //} 497 | } 498 | 499 | // ????? 500 | //if (_this->iWidth) 501 | //{ 502 | // cbMaxWidthHit = _this->iWidth - _this->cbPadding; 503 | //} 504 | 505 | if (!_this->iWidth) 506 | { 507 | //_this->iHeight = cbCurrentTop + _this->cbThumbnailAvailableHeight + _this->cbBottomPadding + _this->cbPadding; !!!!! 508 | _this->iHeight = cbCurrentTop + _sws_WindowSwitcherLayout_GetBottomIncrement(_this) - _sws_WindowSwitcherLayout_GetInitialTop(_this) + _this->cbMasterBottomPadding; 509 | //_this->iWidth = cbMaxWidthHit + _this->cbPadding; !!!!! 510 | _this->iWidth = cbMaxWidthHit + _this->cbMasterRightPadding; 511 | _this->iX = ((_this->mi.rcWork.right - _this->mi.rcWork.left) - _this->iWidth) / 2 + _this->mi.rcWork.left; 512 | _this->iY = ((_this->mi.rcWork.bottom - _this->mi.rcWork.top) - _this->iHeight) / 2 + _this->mi.rcWork.top; 513 | //printf("height: %d, cbCurrentTop: %d, %f %f %f\n", _this->iHeight, cbCurrentTop, _this->cbThumbnailAvailableHeight, _this->cbBottomPadding, _this->cbPadding); 514 | } 515 | 516 | for (int iCurrentWindow = _this->pWindowList.cbSize - 1; iCurrentWindow >= 0; iCurrentWindow--) 517 | { 518 | if (pWindowList[iCurrentWindow].iRowMax) 519 | { 520 | if (pWindowList[iCurrentWindow].hThumbnail) 521 | { 522 | unsigned int diff = (_this->iWidth - _this->cbMasterRightPadding) < pWindowList[iCurrentWindow].iRowMax ? 0 : _this->iWidth - _this->cbMasterRightPadding - pWindowList[iCurrentWindow].iRowMax; 523 | pWindowList[iCurrentWindow].rcThumbnail.left += diff / 2; 524 | pWindowList[iCurrentWindow].rcThumbnail.right += diff / 2; 525 | ////wchar_t name[200]; 526 | ////GetWindowTextW(pWindowList[iCurrentWindow].hWnd, name, 200); 527 | ////wprintf(L"%s %d %d\n", name, pWindowList[iCurrentWindow].rcThumbnail.left, pWindowList[iCurrentWindow].rcThumbnail.right); 528 | 529 | pWindowList[iCurrentWindow].rcWindow.left += diff / 2; 530 | pWindowList[iCurrentWindow].rcWindow.right += diff / 2; 531 | 532 | DWM_THUMBNAIL_PROPERTIES dskThumbProps; 533 | ZeroMemory(&dskThumbProps, sizeof(DWM_THUMBNAIL_PROPERTIES)); 534 | dskThumbProps.dwFlags = DWM_TNP_SOURCECLIENTAREAONLY | DWM_TNP_VISIBLE | DWM_TNP_OPACITY | DWM_TNP_RECTDESTINATION; 535 | dskThumbProps.fSourceClientAreaOnly = FALSE; 536 | dskThumbProps.fVisible = direction; 537 | dskThumbProps.opacity = 255; 538 | dskThumbProps.rcDestination = pWindowList[iCurrentWindow].rcThumbnail; 539 | if (_this->bIncludeWallpaper && pWindowList[iCurrentWindow].hWnd == _this->hWndWallpaper) 540 | { 541 | dskThumbProps.dwFlags |= DWM_TNP_RECTSOURCE; 542 | dskThumbProps.rcSource.left = _this->mi.rcMonitor.left; 543 | dskThumbProps.rcSource.right = _this->mi.rcMonitor.right; 544 | dskThumbProps.rcSource.top = _this->mi.rcMonitor.top; 545 | dskThumbProps.rcSource.bottom = _this->mi.rcMonitor.bottom; 546 | } 547 | HRESULT hr = DwmUpdateThumbnailProperties(pWindowList[iCurrentWindow].hThumbnail, &dskThumbProps); 548 | if (FAILED(hr)) 549 | { 550 | // error 551 | } 552 | } 553 | } 554 | } 555 | } 556 | 557 | if (!rv) 558 | { 559 | /*printf("\n"); 560 | sws_WindowSwitcherLayoutWindow* pWindowList = _this->pWindowList.pList; 561 | for (UINT i = 0; i < _this->pWindowList.cbSize; ++i) 562 | { 563 | TCHAR name[200]; 564 | GetWindowText(pWindowList[i].hWnd, name, 200); 565 | wprintf(L"%d %s\n", pWindowList[i].hWnd, name); 566 | }*/ 567 | } 568 | 569 | return rv; 570 | } 571 | 572 | void sws_WindowSwitcherLayout_Clear(sws_WindowSwitcherLayout* _this) 573 | { 574 | if (_this) 575 | { 576 | DeleteObject(_this->hFontRegular); 577 | DeleteObject(_this->hFontRegular2); 578 | sws_WindowSwitcherLayoutWindow* pWindowList = _this->pWindowList.pList; 579 | if (pWindowList) 580 | { 581 | for (int iCurrentWindow = 0; iCurrentWindow < _this->pWindowList.cbSize; ++iCurrentWindow) 582 | { 583 | sws_WindowSwitcherLayoutWindow_Clear(&(pWindowList[iCurrentWindow])); 584 | } 585 | sws_vector_Clear(&(_this->pWindowList)); 586 | } 587 | memset(_this, 0, sizeof(sws_WindowSwitcherLayout)); 588 | } 589 | } 590 | 591 | sws_error_t sws_WindowSwitcherLayout_Initialize( 592 | sws_WindowSwitcherLayout* _this, 593 | HMONITOR hMonitor, 594 | HWND hWnd, 595 | DWORD* settings, 596 | sws_vector* pHWNDList, 597 | HWND hWndTarget, 598 | HWND hWndWallpaper 599 | ) 600 | { 601 | sws_error_t rv = SWS_ERROR_SUCCESS; 602 | 603 | if (!rv) 604 | { 605 | if (!_this) 606 | { 607 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_NO_MEMORY), NULL); 608 | } 609 | memset(_this, 0, sizeof(sws_WindowSwitcherLayout)); 610 | } 611 | if (!rv) 612 | { 613 | rv = sws_WindowHelpers_Initialize(); 614 | } 615 | if (!rv) 616 | { 617 | rv = sws_vector_Initialize(&(_this->pWindowList), sizeof(sws_WindowSwitcherLayoutWindow)); 618 | } 619 | _this->mi.cbSize = sizeof(MONITORINFO); 620 | if (!rv) 621 | { 622 | if (!GetMonitorInfoW( 623 | hMonitor, 624 | &(_this->mi) 625 | )) 626 | { 627 | rv = sws_error_Report(sws_error_GetFromWin32Error(GetLastError()), NULL); 628 | } 629 | } 630 | if (!rv) 631 | { 632 | _this->bWallpaperAlwaysLast = SWS_WINDOWSWITCHERLAYOUT_WALLPAPER_ALWAYS_LAST; 633 | _this->bIncludeWallpaper = SWS_WINDOWSWITCHERLAYOUT_INCLUDE_WALLPAPER; 634 | if (settings) _this->bIncludeWallpaper = settings[3] && settings[9]; 635 | _this->bWallpaperToggleBehavior = SWS_WINDOWSWITCHERLAYOUT_WALLPAPER_TOGGLE; 636 | _this->hWndWallpaper = hWndWallpaper; 637 | if (_this->bIncludeWallpaper) 638 | { 639 | if (_this->bWallpaperAlwaysLast && !hWndTarget) 640 | { 641 | sws_WindowSwitcherLayoutWindow swsLayoutWindow; 642 | sws_WindowSwitcherLayoutWindow_Initialize(&swsLayoutWindow, _this->hWndWallpaper, NULL); 643 | sws_vector_PushBack(&_this->pWindowList, &swsLayoutWindow); 644 | } 645 | } 646 | } 647 | if (!rv) 648 | { 649 | /*WCHAR wszShellExperienceHostPath[MAX_PATH]; 650 | GetWindowsDirectoryW(wszShellExperienceHostPath, MAX_PATH); 651 | wcscat_s(wszShellExperienceHostPath, MAX_PATH, L"\\SystemApps\\ShellExperienceHost_cw5n1h2txyewy\\ShellExperienceHost.exe"); 652 | WCHAR wszStartMenuExperienceHostPath[MAX_PATH]; 653 | GetWindowsDirectoryW(wszStartMenuExperienceHostPath, MAX_PATH); 654 | wcscat_s(wszStartMenuExperienceHostPath, MAX_PATH, L"\\SystemApps\\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\\StartMenuExperienceHost.exe"); 655 | WCHAR wszSearchHostPath[MAX_PATH]; 656 | GetWindowsDirectoryW(wszSearchHostPath, MAX_PATH); 657 | wcscat_s(wszSearchHostPath, MAX_PATH, L"\\SystemApps\\MicrosoftWindows.Client.CBS_cw5n1h2txyewy\\SearchHost.exe");*/ 658 | if (pHWNDList) 659 | { 660 | wchar_t* targetAUMID = sws_WindowHelpers_GetAUMIDForHWND(hWndTarget); 661 | sws_window* windowList = pHWNDList->pList; 662 | sws_window* window = NULL; 663 | if (hWndTarget) 664 | { 665 | for (int i = 0; i < pHWNDList->cbSize; ++i) 666 | { 667 | if (windowList[i].hWnd == hWndTarget) 668 | { 669 | window = &(windowList[i]); 670 | break; 671 | } 672 | } 673 | } 674 | //if (hWndTarget && window && window->bIsApplicationFrameHost) 675 | //{ 676 | // sws_WindowSwitcherLayoutWindow swsLayoutWindow; 677 | // sws_WindowSwitcherLayoutWindow_Initialize(&swsLayoutWindow, window->hWnd, window->wszPath); 678 | // sws_vector_PushBack(&_this->pWindowList, &swsLayoutWindow); 679 | //} 680 | //else 681 | { 682 | WCHAR wszRundll32Path[MAX_PATH]; 683 | GetSystemDirectoryW(wszRundll32Path, MAX_PATH); 684 | wcscat_s(wszRundll32Path, MAX_PATH, L"\\rundll32.exe"); 685 | //HWND hWndForeground = GetForegroundWindow(); 686 | for (int i = pHWNDList->cbSize - 1; i >= 0; i--) 687 | { 688 | /*if (!_wcsicmp(wszShellExperienceHostPath, windowList[i].wszPath) || 689 | !_wcsicmp(wszStartMenuExperienceHostPath, windowList[i].wszPath) || 690 | !_wcsicmp(wszSearchHostPath, windowList[i].wszPath)) 691 | { 692 | continue; 693 | }*/ 694 | BOOL isCloaked; 695 | DwmGetWindowAttribute(windowList[i].hWnd, DWMWA_CLOAKED, &isCloaked, sizeof(BOOL)); 696 | if (isCloaked) 697 | { 698 | continue; 699 | /*if (!windowList[i].bIsApplicationFrameHost) 700 | { 701 | continue; 702 | } 703 | if (windowList[i].bIsApplicationFrameHost && windowList[i].hWnd != hWndForeground) 704 | { 705 | continue; 706 | }*/ 707 | } 708 | if (hWndTarget && hWndTarget != windowList[i].hWnd) 709 | { 710 | if (targetAUMID) 711 | { 712 | if (!(windowList[i].wszAUMID && !wcscmp(targetAUMID, windowList[i].wszAUMID))) continue; 713 | } 714 | else 715 | { 716 | if (!window) 717 | { 718 | continue; 719 | } 720 | else if (window->dwProcessId != windowList[i].dwProcessId && _wcsicmp(window->wszPath, windowList[i].wszPath)) 721 | { 722 | continue; 723 | } 724 | } 725 | } 726 | if (!hWndTarget && settings[10] && _wcsicmp(windowList[i].wszPath, wszRundll32Path)) 727 | { 728 | BOOL bShouldContinue = FALSE; 729 | for (int j = i - 1; j >= 0; j--) 730 | { 731 | if (sws_WindowHelpers_IsAltTabWindow(windowList[j].hWnd) && windowList[i].wszAUMID && windowList[j].wszAUMID) 732 | { 733 | if (!wcscmp(windowList[i].wszAUMID, windowList[j].wszAUMID) && (settings[4] ? MonitorFromWindow(windowList[i].hWnd, MONITOR_DEFAULTTONULL) == MonitorFromWindow(windowList[j].hWnd, MONITOR_DEFAULTTONULL) : TRUE)) 734 | { 735 | windowList[j].pNextWindow = windowList + i; 736 | bShouldContinue = TRUE; 737 | break; 738 | } 739 | } 740 | else if (sws_WindowHelpers_IsAltTabWindow(windowList[j].hWnd) && 741 | (windowList[i].dwProcessId == windowList[j].dwProcessId || !_wcsicmp(windowList[i].wszPath, windowList[j].wszPath)) && 742 | (settings[4] ? MonitorFromWindow(windowList[i].hWnd, MONITOR_DEFAULTTONULL) == MonitorFromWindow(windowList[j].hWnd, MONITOR_DEFAULTTONULL) : TRUE)) 743 | { 744 | bShouldContinue = TRUE; 745 | break; 746 | } 747 | } 748 | if (bShouldContinue) 749 | { 750 | continue; 751 | } 752 | } 753 | if (settings[4] && hMonitor != MonitorFromWindow(windowList[i].hWnd, MONITOR_DEFAULTTONULL)) 754 | { 755 | continue; 756 | } 757 | sws_WindowSwitcherLayoutWindow swsLayoutWindow; 758 | sws_WindowSwitcherLayoutWindow_Initialize(&swsLayoutWindow, windowList[i].hWnd, windowList[i].wszPath); 759 | for (sws_window* pcw = windowList + i; pcw != NULL; pcw = pcw->pNextWindow) sws_WindowSwitcherLayoutWindow_AddGroupedWnd(&swsLayoutWindow, pcw->hWnd); 760 | sws_vector_PushBack(&_this->pWindowList, &swsLayoutWindow); 761 | } 762 | } 763 | if (targetAUMID) CoTaskMemFree(targetAUMID); 764 | } 765 | } 766 | ////if (!rv) 767 | ////{ 768 | //// rv = sws_WindowHelpers_RealEnumWindows((WNDENUMPROC)_sws_WindowSwitcherLayout_EnumWindowsCallback, (LPARAM)_this); 769 | ////} 770 | 771 | _this->cbMaxHeight = 0; 772 | _this->cbMaxWidth = 0; 773 | _this->cbRowWidth = 0; 774 | _this->cbRowHeight = SWS_WINDOWSWITCHERLAYOUT_ROWHEIGHT; 775 | if (settings) _this->cbRowHeight = settings[0]; 776 | _this->cbRowTitleHeight = SWS_WINDOWSWITCHERLAYOUT_ROWTITLEHEIGHT; 777 | _this->cbMasterTopPadding = SWS_WINDOWSWITCHERLAYOUT_MASTER_PADDING_TOP; 778 | if (settings) _this->cbMasterTopPadding = settings[8]; 779 | _this->cbMasterBottomPadding = SWS_WINDOWSWITCHERLAYOUT_MASTER_PADDING_BOTTOM; 780 | if (settings) _this->cbMasterBottomPadding = _this->cbMasterTopPadding; 781 | _this->cbMasterLeftPadding = SWS_WINDOWSWITCHERLAYOUT_MASTER_PADDING_LEFT; 782 | if (settings) _this->cbMasterLeftPadding = _this->cbMasterTopPadding; 783 | _this->cbMasterRightPadding = SWS_WINDOWSWITCHERLAYOUT_MASTER_PADDING_RIGHT; 784 | if (settings) _this->cbMasterRightPadding = _this->cbMasterTopPadding; 785 | _this->cbElementTopPadding = SWS_WINDOWSWITCHERLAYOUT_ELEMENT_PADDING_TOP; 786 | _this->cbElementBottomPadding = SWS_WINDOWSWITCHERLAYOUT_ELEMENT_PADDING_BOTTOM; 787 | _this->cbElementLeftPadding = SWS_WINDOWSWITCHERLAYOUT_ELEMENT_PADDING_LEFT; 788 | _this->cbElementRightPadding = SWS_WINDOWSWITCHERLAYOUT_ELEMENT_PADDING_RIGHT; 789 | _this->cbTopPadding = SWS_WINDOWSWITCHERLAYOUT_PADDING_TOP; 790 | _this->cbBottomPadding = SWS_WINDOWSWITCHERLAYOUT_PADDING_BOTTOM; 791 | _this->cbLeftPadding = SWS_WINDOWSWITCHERLAYOUT_PADDING_LEFT; 792 | _this->cbRightPadding = SWS_WINDOWSWITCHERLAYOUT_PADDING_RIGHT; 793 | _this->cbHDividerPadding = SWS_WINDOWSWITCHERLAYOUT_PADDING_DIVIDER_HORIZONTAL; 794 | _this->cbHDividerPadding = SWS_WINDOWSWITCHERLAYOUT_PADDING_DIVIDER_HORIZONTAL; 795 | _this->cbMaxTileWidth = _this->cbRowHeight * SWS_WINDOWSWITCHERLAYOUT_MAX_TILE_WIDTH; 796 | _this->cbThumbnailAvailableHeight = 0; 797 | _this->hWnd = hWnd; 798 | _this->hMonitor = hMonitor; 799 | _this->iIndex = _this->pWindowList.cbSize - 1; 800 | 801 | if (!rv) 802 | { 803 | int pw = SWS_WINDOWSWITCHERLAYOUT_PERCENTAGEWIDTH; 804 | if (settings) pw = settings[1]; 805 | _this->cbMaxWidth = (unsigned int)((double)(_this->mi.rcWork.right - _this->mi.rcWork.left) * (pw / 100.0)); 806 | if (settings && settings[5] != 0 && _this->cbMaxWidth > settings[5]) 807 | { 808 | _this->cbMaxWidth = settings[5]; 809 | } 810 | int ph = SWS_WINDOWSWITCHERLAYOUT_PERCENTAGEHEIGHT; 811 | if (settings) ph = settings[2]; 812 | _this->cbMaxHeight = (unsigned int)((double)(_this->mi.rcWork.bottom - _this->mi.rcWork.top) * (ph / 100.0)); 813 | if (settings && settings[6] != 0 && _this->cbMaxHeight > settings[6]) 814 | { 815 | _this->cbMaxHeight = settings[6]; 816 | } 817 | 818 | HRESULT hr = GetDpiForMonitor( 819 | hMonitor, 820 | MDT_DEFAULT, 821 | &(_this->cbDpiX), 822 | &(_this->cbDpiY) 823 | ); 824 | rv = sws_error_Report(sws_error_GetFromHRESULT(hr), NULL); 825 | 826 | _this->cbRowHeight *= (_this->cbDpiY / DEFAULT_DPI_Y); 827 | _this->cbRowTitleHeight *= (_this->cbDpiY / DEFAULT_DPI_X); 828 | _this->cbMasterTopPadding *= (_this->cbDpiY / DEFAULT_DPI_Y); 829 | _this->cbMasterLeftPadding *= (_this->cbDpiX / DEFAULT_DPI_X); 830 | _this->cbMasterBottomPadding *= (_this->cbDpiY / DEFAULT_DPI_Y); 831 | _this->cbMasterRightPadding *= (_this->cbDpiX / DEFAULT_DPI_X); 832 | _this->cbTopPadding *= (_this->cbDpiY / DEFAULT_DPI_Y); 833 | _this->cbLeftPadding *= (_this->cbDpiX / DEFAULT_DPI_X); 834 | _this->cbBottomPadding *= (_this->cbDpiY / DEFAULT_DPI_Y); 835 | _this->cbRightPadding *= (_this->cbDpiX / DEFAULT_DPI_X); 836 | _this->cbElementTopPadding *= (_this->cbDpiY / DEFAULT_DPI_Y); 837 | _this->cbElementLeftPadding *= (_this->cbDpiX / DEFAULT_DPI_X); 838 | _this->cbElementBottomPadding *= (_this->cbDpiY / DEFAULT_DPI_Y); 839 | _this->cbElementRightPadding *= (_this->cbDpiX / DEFAULT_DPI_X); 840 | _this->cbHDividerPadding *= (_this->cbDpiY / DEFAULT_DPI_Y); 841 | _this->cbVDividerPadding *= (_this->cbDpiX / DEFAULT_DPI_X); 842 | _this->cbMaxTileWidth *= (_this->cbDpiX / DEFAULT_DPI_X); 843 | 844 | _this->cbThumbnailAvailableHeight = _this->cbRowHeight - _this->cbRowTitleHeight - _this->cbTopPadding - 2 * _this->cbBottomPadding; 845 | } 846 | if (!rv) 847 | { 848 | sws_WindowSwitcherLayoutWindow* pWindowList = _this->pWindowList.pList; 849 | for (int iCurrentWindow = _this->pWindowList.cbSize - 1; iCurrentWindow >= 0; iCurrentWindow--) 850 | { 851 | if (!pWindowList[iCurrentWindow].hIcon) 852 | { 853 | double factor = SWS_UWP_ICON_SCALE_FACTOR; 854 | pWindowList[iCurrentWindow].rcIcon.left = 0; 855 | pWindowList[iCurrentWindow].rcIcon.top = 0; 856 | pWindowList[iCurrentWindow].rcIcon.right = _this->cbRowTitleHeight * (SWS_WINDOWSWITCHERLAYOUT_PERCENTAGEICON / 100.0); 857 | pWindowList[iCurrentWindow].rcIcon.bottom = pWindowList[iCurrentWindow].rcIcon.right; 858 | pWindowList[iCurrentWindow].szIcon = pWindowList[iCurrentWindow].rcIcon.right; 859 | pWindowList[iCurrentWindow].hIcon = sws_DefAppIcon; 860 | /*sws_IconPainter_CallbackParams* params = malloc(sizeof(sws_IconPainter_CallbackParams)); 861 | if (params) 862 | { 863 | params->hWnd = hWnd; 864 | params->index = iCurrentWindow; 865 | if (!_this->timestamp) 866 | { 867 | _this->timestamp = sws_milliseconds_now(); 868 | } 869 | params->timestamp = _this->timestamp; 870 | params->bIsDesktop = (_this->bIncludeWallpaper && pWindowList[iCurrentWindow].hWnd == _this->hWndWallpaper); 871 | if (!sws_IconPainter_ExtractAndDrawIconAsync(pWindowList[iCurrentWindow].hWnd, params)) 872 | { 873 | pWindowList[iCurrentWindow].hIcon = sws_DefAppIcon; 874 | free(params); 875 | } 876 | }*/ 877 | } 878 | } 879 | } 880 | if (!rv) 881 | { 882 | LOGFONT logFont; 883 | memset(&logFont, 0, sizeof(logFont)); 884 | logFont.lfHeight = SWS_FONT_SIZE * (_this->cbDpiY / DEFAULT_DPI_Y); 885 | wcscpy_s(logFont.lfFaceName, 32, _T(SWS_FONT_NAME)); 886 | logFont.lfWeight = FW_REGULAR; 887 | _this->hFontRegular = CreateFontIndirectW(&logFont); 888 | if (!_this->hFontRegular) 889 | { 890 | rv = sws_error_Report(sws_error_GetFromWin32Error(GetLastError()), NULL); 891 | } 892 | logFont.lfHeight = SWS_FONT_SIZE2 * (_this->cbDpiY / DEFAULT_DPI_Y); 893 | _this->hFontRegular2 = CreateFontIndirectW(&logFont); 894 | if (!_this->hFontRegular2) 895 | { 896 | rv = sws_error_Report(sws_error_GetFromWin32Error(GetLastError()), NULL); 897 | } 898 | } 899 | 900 | return rv; 901 | } 902 | -------------------------------------------------------------------------------- /SimpleWindowSwitcher/sws_WindowSwitcherLayout.h: -------------------------------------------------------------------------------- 1 | #ifndef _H_SWS_WINDOWSWITCHERLAYOUT_H_ 2 | #define _H_SWS_WINDOWSWITCHERLAYOUT_H_ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #pragma comment(lib, "Dwmapi.lib") 8 | #include 9 | #pragma comment(lib, "Shcore.lib") 10 | #include "sws_def.h" 11 | #include "sws_error.h" 12 | #include "sws_vector.h" 13 | #include "sws_window.h" 14 | #include "sws_utility.h" 15 | #include "sws_WindowSwitcherLayoutWindow.h" 16 | #include "sws_WindowHelpers.h" 17 | #include "sws_IconPainter.h" 18 | 19 | typedef struct _sws_WindowSwitcherLayout 20 | { 21 | HMONITOR hMonitor; 22 | HWND hWnd; 23 | 24 | sws_vector pWindowList; 25 | int iX; 26 | int iY; 27 | unsigned int iWidth; 28 | unsigned int iHeight; 29 | unsigned int cbDpiX; 30 | unsigned int cbDpiY; 31 | int iIndex; 32 | MONITORINFO mi; 33 | unsigned int cbMaxHeight; 34 | unsigned int cbMaxWidth; 35 | unsigned int cbRowWidth; 36 | unsigned int cbRowHeight; 37 | unsigned int cbRowTitleHeight; 38 | unsigned int cbMasterTopPadding; 39 | unsigned int cbMasterBottomPadding; 40 | unsigned int cbMasterLeftPadding; 41 | unsigned int cbMasterRightPadding; 42 | unsigned int cbElementTopPadding; 43 | unsigned int cbElementBottomPadding; 44 | unsigned int cbElementLeftPadding; 45 | unsigned int cbElementRightPadding; 46 | unsigned int cbTopPadding; 47 | unsigned int cbBottomPadding; 48 | unsigned int cbLeftPadding; 49 | unsigned int cbRightPadding; 50 | unsigned int cbHDividerPadding; 51 | unsigned int cbVDividerPadding; 52 | unsigned int cbThumbnailAvailableHeight; 53 | unsigned int cbMaxTileWidth; 54 | unsigned int numTopMost; 55 | BOOL bIncludeWallpaper; 56 | BOOL bWallpaperAlwaysLast; 57 | BOOL bWallpaperToggleBehavior; 58 | HWND hWndWallpaper; 59 | HFONT hFontRegular; 60 | HFONT hFontRegular2; 61 | long long timestamp; 62 | } sws_WindowSwitcherLayout; 63 | 64 | static BOOL CALLBACK _sws_WindowSwitcherLayout_EnumWindowsCallback(_In_ HWND hWnd, _In_ sws_WindowSwitcherLayout* _this); 65 | 66 | sws_error_t sws_WindowSwitcherLayout_InvalidateLayout(sws_WindowSwitcherLayout* _this); 67 | 68 | sws_error_t sws_WindowSwitcherLayout_ComputeLayout(sws_WindowSwitcherLayout* _this, int direction, HWND hTarget); 69 | 70 | void sws_WindowSwitcherLayout_Clear(sws_WindowSwitcherLayout* _this); 71 | 72 | sws_error_t sws_WindowSwitcherLayout_Initialize( 73 | sws_WindowSwitcherLayout* _this, 74 | HMONITOR hMonitor, 75 | HWND hWnd, 76 | DWORD* settings, 77 | sws_vector* pHWNDList, 78 | HWND hWndTarget, 79 | HWND hWndWallpaper 80 | ); 81 | 82 | #endif -------------------------------------------------------------------------------- /SimpleWindowSwitcher/sws_WindowSwitcherLayoutWindow.c: -------------------------------------------------------------------------------- 1 | #include "sws_WindowSwitcherLayoutWindow.h" 2 | 3 | int sws_WindowSwitcherLayoutWindow_AddGroupedWnd(sws_WindowSwitcherLayoutWindow* _this, HWND hWnd) 4 | { 5 | int rv = DPA_AppendPtr(_this->dpaGroupedWnds, hWnd); 6 | if (rv != -1) _this->dwCount++; 7 | return rv; 8 | } 9 | 10 | void sws_WindowSwitcherLayoutWindow_Erase(sws_WindowSwitcherLayoutWindow* _this) 11 | { 12 | if (_this->hThumbnail) 13 | { 14 | sws_error_Report(sws_error_GetFromHRESULT(DwmUnregisterThumbnail(_this->hThumbnail)), NULL); 15 | _this->hThumbnail = 0; 16 | } 17 | SIZE siz; 18 | siz.cx = 0; 19 | siz.cy = 0; 20 | _this->sizWindow = siz; 21 | RECT rc; 22 | rc.left = 0; 23 | rc.top = 0; 24 | rc.bottom = 0; 25 | rc.right = 0; 26 | _this->rcThumbnail = rc; 27 | _this->rcWindow = rc; 28 | _this->iRowMax = 0; 29 | } 30 | 31 | void sws_WindowSwitcherLayoutWindow_Clear(sws_WindowSwitcherLayoutWindow* _this) 32 | { 33 | if (_this->hThumbnail) 34 | { 35 | DwmUnregisterThumbnail(_this->hThumbnail); 36 | _this->hThumbnail = 0; 37 | } 38 | //if (_this->hIcon && !_this->bOwnProcess) 39 | if (_this->hIcon && sws_DefAppIcon && _this->hIcon != sws_DefAppIcon && sws_LegacyDefAppIcon && _this->hIcon != sws_LegacyDefAppIcon) 40 | { 41 | DestroyIcon(_this->hIcon); 42 | } 43 | if (_this->dpaGroupedWnds) 44 | { 45 | DPA_Destroy(_this->dpaGroupedWnds); 46 | } 47 | if (_this->wszAUMID) 48 | { 49 | CoTaskMemFree(_this->wszAUMID); 50 | } 51 | memset(_this, 0, sizeof(sws_WindowSwitcherLayoutWindow)); 52 | } 53 | 54 | sws_error_t sws_WindowSwitcherLayoutWindow_Initialize(sws_WindowSwitcherLayoutWindow* _this, HWND hWnd, WCHAR* wszPath) 55 | { 56 | sws_error_t rv = SWS_ERROR_SUCCESS; 57 | 58 | if (!rv) 59 | { 60 | if (!_this) 61 | { 62 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_NO_MEMORY), NULL); 63 | } 64 | } 65 | if (!rv) 66 | { 67 | memset(_this, 0, sizeof(sws_WindowSwitcherLayoutWindow)); 68 | } 69 | if (!rv) 70 | { 71 | _this->hWnd = hWnd; 72 | _this->dwCount = 0; 73 | } 74 | if (!rv && wszPath) 75 | { 76 | wcscpy_s(_this->wszPath, MAX_PATH, wszPath); 77 | } 78 | if (!rv) 79 | { 80 | _this->wszAUMID = sws_WindowHelpers_GetAUMIDForHWND(_this->hWnd); 81 | } 82 | if (!rv) 83 | { 84 | _this->dpaGroupedWnds = DPA_Create(SWS_VECTOR_CAPACITY); 85 | if (!_this->dpaGroupedWnds) 86 | { 87 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_NO_MEMORY), NULL); 88 | } 89 | } 90 | 91 | return rv; 92 | } -------------------------------------------------------------------------------- /SimpleWindowSwitcher/sws_WindowSwitcherLayoutWindow.h: -------------------------------------------------------------------------------- 1 | #ifndef _H_SWS_WINDOWSWITCHERLAYOUTWINDOW_ 2 | #define _H_SWS_WINDOWSWITCHERLAYOUTWINDOW_ 3 | #include 4 | #include 5 | #pragma comment(lib, "Dwmapi.lib") 6 | #include "sws_error.h" 7 | #include "sws_WindowHelpers.h" 8 | #include "sws_tshwnd.h" 9 | 10 | typedef struct _sws_WindowSwitcherLayoutWindow 11 | { 12 | HWND hWnd; 13 | SIZE sizWindow; 14 | HTHUMBNAIL hThumbnail; 15 | RECT rcThumbnail; 16 | RECT rcWindow; 17 | int iRowMax; 18 | HICON hIcon; 19 | UINT dwIconSource; 20 | UINT szIcon; 21 | RECT rcIcon; 22 | WCHAR wszPath[MAX_PATH]; 23 | sws_tshwnd* tshWnd; 24 | sws_tshwnd* last_flashing_tshwnd; 25 | DWORD dwCount; 26 | DWORD dwWindowFlags; 27 | WCHAR* wszAUMID; 28 | HDPA dpaGroupedWnds; 29 | } sws_WindowSwitcherLayoutWindow; 30 | 31 | int sws_WindowSwitcherLayoutWindow_AddGroupedWnd(sws_WindowSwitcherLayoutWindow* _this, HWND hWnd); 32 | 33 | void sws_WindowSwitcherLayoutWindow_Erase(sws_WindowSwitcherLayoutWindow* _this); 34 | 35 | void sws_WindowSwitcherLayoutWindow_Clear(sws_WindowSwitcherLayoutWindow* _this); 36 | 37 | sws_error_t sws_WindowSwitcherLayoutWindow_Initialize(sws_WindowSwitcherLayoutWindow* _this, HWND hWnd, WCHAR* wszPath); 38 | #endif -------------------------------------------------------------------------------- /SimpleWindowSwitcher/sws_def.h: -------------------------------------------------------------------------------- 1 | #ifndef _H_SWS_DEF_H_ 2 | #define _H_SWS_DEF_H_ 3 | #define UNICODE 4 | #include 5 | 6 | #define SWS_GUID_TEXTUAL "{BEA057BB-66C7-4758-A610-FAE6013E9F98}" 7 | 8 | DEFINE_GUID(LiveSetting_Property_GUID, 0xc12bcd8e, 0x2a8e, 0x4950, 0x8a, 0xe7, 0x36, 0x25, 0x11, 0x1d, 0x58, 0xeb); 9 | 10 | #ifndef NTDDI_WIN10_CO 11 | #define DWMWA_USE_HOSTBACKDROPBRUSH 17 // [set] BOOL, Allows the use of host backdrop brushes for the window. 12 | #define DWMWA_USE_IMMERSIVE_DARK_MODE 20 // [set] BOOL, Allows a window to either use the accent color, or dark, according to the user Color Mode preferences. 13 | #define DWMWA_WINDOW_CORNER_PREFERENCE 33 // [set] WINDOW_CORNER_PREFERENCE, Controls the policy that rounds top-level window corners 14 | #define DWMWA_BORDER_COLOR 34 // [set] COLORREF, The color of the thin border around a top-level window 15 | #define DWMWA_CAPTION_COLOR 35 // [set] COLORREF, The color of the caption 16 | #define DWMWA_TEXT_COLOR 36 // [set] COLORREF, The color of the caption text 17 | #define DWMWA_VISIBLE_FRAME_BORDER_THICKNESS 37 // [get] UINT, width of the visible border around a thick frame window 18 | 19 | #define DWMWCP_DEFAULT 0 20 | #define DWMWCP_DONOTROUND 1 21 | #define DWMWCP_ROUND 2 22 | #define DWMWCP_ROUNDSMALL 3 23 | #endif 24 | 25 | #define DWMWA_MICA_EFFFECT 1029 26 | 27 | #define DEFAULT_DPI_X 96.0 28 | #define DEFAULT_DPI_Y 96.0 29 | 30 | #define SWS_UWP_ICON_SCALE_FACTOR 0.7 31 | 32 | #define SWS_WINDOWSWITCHER_THEME_NONE 0 33 | #define SWS_WINDOWSWITCHER_THEME_BACKDROP 1 34 | #define SWS_WINDOWSWITCHER_THEME_MICA 2 35 | 36 | #define SWS_WINDOWSWITCHER_FLASH_COLOR RGB(255, 114, 0) 37 | inline RGBQUAD sws_GetFlashRGB(BOOL bIsDarkMode) 38 | { 39 | double alpha = 0.9; 40 | RGBQUAD rgbFlash = { 41 | GetBValue(SWS_WINDOWSWITCHER_FLASH_COLOR) * alpha, 42 | GetGValue(SWS_WINDOWSWITCHER_FLASH_COLOR) * alpha, 43 | GetRValue(SWS_WINDOWSWITCHER_FLASH_COLOR) * alpha, 44 | alpha * 255 45 | }; 46 | return rgbFlash; 47 | } 48 | 49 | #define SWS_WINDOWSWITCHER_CLASSNAME "SimpleWindowSwitcher_" SWS_GUID_TEXTUAL 50 | #define SWS_WINDOWSWITCHER_BACKGROUND_COLOR RGB(32, 32, 32) 51 | #define SWS_WINDOWSWITCHER_BACKGROUND_COLOR_LIGHT RGB(243, 243, 243) 52 | #define SWS_WINDOWSWITCHER_CONTOUR_COLOR RGB(255, 255, 255) 53 | #define SWS_WINDOWSWITCHER_CONTOUR_COLOR_LIGHT RGB(0, 0, 0) 54 | #define SWS_WINDOWSWITCHER_TEXT_COLOR RGB(255, 255, 255) 55 | #define SWS_WINDOWSWITCHER_TEXT_COLOR_LIGHT RGB(0, 0, 0) 56 | #define SWS_WINDOWSWITCHER_CLOSE_COLOR RGB(196, 43, 28) 57 | #define SWS_WINDOWSWITCHER_CONTOUR_SIZE 2 58 | #define SWS_WINDOWSWITCHER_HIGHLIGHT_SIZE 1 59 | #define SWS_WINDOWSWITCHER_THEME_CLASS "ControlPanelStyle" 60 | #define SWS_WINDOWSWITCHER_THEME_INDEX 8 61 | #define SWS_FONT_NAME "Segoe UI" 62 | #define SWS_FONT_SIZE -12 63 | #define SWS_FONT_SIZE2 -24 64 | 65 | #define SWS_WINDOWFLAG_IS_ON_WINDOW 0b001 66 | #define SWS_WINDOWFLAG_IS_ON_THUMBNAIL 0b010 67 | #define SWS_WINDOWFLAG_IS_ON_CLOSE 0b100 68 | 69 | #define SWS_CONTOUR_INNER 1 70 | #define SWS_CONTOUR_OUTER -1 71 | 72 | #define SWS_WINDOWSWITCHERLAYOUT_INCLUDE_WALLPAPER TRUE 73 | #define SWS_WINDOWSWITCHERLAYOUT_WALLPAPER_ALWAYS_LAST TRUE 74 | #define SWS_WINDOWSWITCHERLAYOUT_WALLPAPER_TOGGLE TRUE 75 | #define SWS_WINDOWSWITCHERLAYOUT_PERCENTAGEWIDTH 80 76 | #define SWS_WINDOWSWITCHERLAYOUT_PERCENTAGEHEIGHT 80 77 | #define SWS_WINDOWSWITCHERLAYOUT_ROWHEIGHT 230 78 | #define SWS_WINDOWSWITCHERLAYOUT_MAX_TILE_WIDTH 2.0 79 | 80 | #define SWS_WINDOWSWITCHERLAYOUT_MASTER_PADDING_TOP 20 81 | #define SWS_WINDOWSWITCHERLAYOUT_MASTER_PADDING_LEFT SWS_WINDOWSWITCHERLAYOUT_MASTER_PADDING_TOP 82 | #define SWS_WINDOWSWITCHERLAYOUT_MASTER_PADDING_BOTTOM SWS_WINDOWSWITCHERLAYOUT_MASTER_PADDING_TOP 83 | #define SWS_WINDOWSWITCHERLAYOUT_MASTER_PADDING_RIGHT SWS_WINDOWSWITCHERLAYOUT_MASTER_PADDING_TOP 84 | 85 | #define SWS_WINDOWSWITCHERLAYOUT_ELEMENT_PADDING_TOP 5 86 | #define SWS_WINDOWSWITCHERLAYOUT_ELEMENT_PADDING_LEFT 2 87 | #define SWS_WINDOWSWITCHERLAYOUT_ELEMENT_PADDING_BOTTOM 5 88 | #define SWS_WINDOWSWITCHERLAYOUT_ELEMENT_PADDING_RIGHT 2 89 | 90 | #define SWS_WINDOWSWITCHERLAYOUT_PADDING_TOP 7 91 | #define SWS_WINDOWSWITCHERLAYOUT_PADDING_LEFT SWS_WINDOWSWITCHERLAYOUT_PADDING_TOP 92 | #define SWS_WINDOWSWITCHERLAYOUT_PADDING_BOTTOM SWS_WINDOWSWITCHERLAYOUT_PADDING_TOP 93 | #define SWS_WINDOWSWITCHERLAYOUT_PADDING_RIGHT SWS_WINDOWSWITCHERLAYOUT_PADDING_TOP 94 | 95 | #define SWS_WINDOWSWITCHERLAYOUT_PADDING_DIVIDER_HORIZONTAL SWS_WINDOWSWITCHERLAYOUT_PADDING_TOP 96 | #define SWS_WINDOWSWITCHERLAYOUT_PADDING_DIVIDER_VERTICAL SWS_WINDOWSWITCHERLAYOUT_PADDING_LEFT 97 | 98 | #define SWS_WINDOWSWITCHERLAYOUT_ROWTITLEHEIGHT 30 99 | #define SWS_WINDOWSWITCHERLAYOUT_PERCENTAGEICON 70 100 | #define SWS_WINDOWSWITCHERLAYOUT_COMPUTE_DIRECTION_INITIAL 0 101 | #define SWS_WINDOWSWITCHERLAYOUT_COMPUTE_DIRECTION_FORWARD 1 102 | #define SWS_WINDOWSWITCHERLAYOUT_COMPUTE_DIRECTION_BACKWARD -1 103 | 104 | #define SWS_WINDOWSWITCHERLAYOUT_EMPTYWINDOW_THUMBNAIL_WIDTH 215 105 | #define SWS_WINDOWSWITCHERLAYOUT_EMPTYWINDOW_THUMBNAIL_HEIGHT 144 106 | #define SWS_WINDOWSWITCHERLAYOUT_EMPTYWINDOW_TITLECOLOR RGB(153, 153, 153) 107 | #define SWS_WINDOWSWITCHERLAYOUT_EMPTYWINDOW_GRADIENT_UP RGB(255, 255, 255) 108 | #define SWS_WINDOWSWITCHERLAYOUT_EMPTYWINDOW_GRADIENT_DOWN SWS_WINDOWSWITCHERLAYOUT_EMPTYWINDOW_TITLECOLOR 109 | #define SWS_WINDOWSWITCHERLAYOUT_EMPTYWINDOW_THUMBNAILPADDING_TOP 1 110 | #define SWS_WINDOWSWITCHERLAYOUT_EMPTYWINDOW_THUMBNAILPADDING_LEFT 7 111 | #define SWS_WINDOWSWITCHERLAYOUT_EMPTYWINDOW_THUMBNAILPADDING_BOTTOM SWS_WINDOWSWITCHERLAYOUT_EMPTYWINDOW_THUMBNAILPADDING_LEFT 112 | #define SWS_WINDOWSWITCHERLAYOUT_EMPTYWINDOW_THUMBNAILPADDING_RIGHT SWS_WINDOWSWITCHERLAYOUT_EMPTYWINDOW_THUMBNAILPADDING_LEFT 113 | 114 | #define SWS_WINDOWSWITCHERLAYOUT_THUMBNAIL_MINWIDTH 115 115 | #define SWS_WINDOWSWITCHERLAYOUT_THUMBNAIL_MINHEIGHT 0 116 | 117 | #define SWS_WINDOWSWITCHERLAYOUT_WINDOWFLAGS_ISUWP 0b00000001 118 | #define SWS_WINDOWSWITCHERLAYOUT_WINDOWFLAGS_ISEMPTY 0b00000010 119 | #define SWS_WINDOWSWITCHERLAYOUT_WINDOWFLAGS_ISTOOSMALLHORIZONTAL 0b00000100 120 | #define SWS_WINDOWSWITCHERLAYOUT_WINDOWFLAGS_ISTOOSMALLVERTICAL 0b00001000 121 | 122 | #define SWS_WINDOWSWITCHER_LAYOUTMODE_FULL 0 123 | #define SWS_WINDOWSWITCHER_LAYOUTMODE_MINI 1 124 | 125 | #define SWS_WINDOWSWITCHER_TIMER_SHOW 10 126 | #define SWS_WINDOWSWITCHER_TIMER_TOGGLE_DESKTOP 11 127 | #define SWS_WINDOWSWITCHER_TIMER_TOGGLE_DESKTOP_DELAY 500 128 | #define SWS_WINDOWSWITCHER_TIMER_STARTUP 12 129 | #define SWS_WINDOWSWITCHER_TIMER_STARTUP_DELAY 3000 130 | #define SWS_WINDOWSWITCHER_TIMER_PEEKATDESKTOP 13 131 | #define SWS_WINDOWSWITCHER_TIMER_PEEKATDESKTOP_DELAY 500 132 | #define SWS_WINDOWSWITCHER_TIMER_ASYNCKEYCHECK 14 133 | #define SWS_WINDOWSWITCHER_TIMER_ASYNCKEYCHECK_DELAY 100 134 | #define SWS_WINDOWSWITCHER_TIMER_UPDATEACCESSIBLETEXT 15 135 | #define SWS_WINDOWSWITCHER_TIMER_UPDATEACCESSIBLETEXT_DELAY 100 136 | #define SWS_WINDOWSWITCHER_TIMER_PAINT 16 137 | #define SWS_WINDOWSWITCHER_TIMER_PAINT_GETICONASYNC_DELAY 500 138 | #define SWS_WINDOWSWITCHER_TIMER_CLOSEHWND 17 139 | #define SWS_WINDOWSWITCHER_TIMER_CLOSEHWND_DELAY 300 140 | 141 | #define SWS_WINDOWSWITCHER_ANIMATOR_FLASH_DELAY 25 142 | #define SWS_WINDOWSWITCHER_ANIMATOR_FLASH_STEP 0.05 143 | #define SWS_WINDOWSWITCHER_ANIMATOR_FLASH_MAXSTATE 9 144 | 145 | #define SWS_WINDOWSWITCHER_PAINT_MSG (WM_APP + 1) 146 | 147 | #define SWS_WINDOWSWITCHER_PAINTFLAGS_NONE 0b000 148 | #define SWS_WINDOWSWITCHER_PAINTFLAGS_REDRAWENTIRE 0b001 149 | #define SWS_WINDOWSWITCHER_PAINTFLAGS_ISFLASHANIMATION 0b010 150 | #define SWS_WINDOWSWITCHER_PAINTFLAGS_ACTIVEMASKORINDEXCHANGED 0b100 151 | 152 | 153 | #define SWS_WINDOWSWITCHER_SHOWDELAY 100 154 | #define SWS_WINDOWSWITCHER_MAX_NUM_MONITORS 20 155 | #define SWS_WINDOWSWITCHER_MAX_NUM_WINDOWS 100 156 | 157 | #define SWS_VECTOR_CAPACITY 500 158 | 159 | #define SWS_SORT_DESCENDING 0b1 160 | #define SWS_SORT_ASCENDING 0b0 161 | 162 | #define SWS_WALLPAPERSUPPORT_NONE 0 163 | #define SWS_WALLPAPERSUPPORT_EXPLORER 1 164 | 165 | #define SWS_SCROLLWHEELBEHAVIOR_DISABLED 0 166 | #define SWS_SCROLLWHEELBEHAVIOR_ONLYCLIENTAREA 1 167 | #define SWS_SCROLLWHEELBEHAVIOR_EVERYWHERE 2 168 | 169 | DEFINE_GUID(sws_CLSID_InputSwitchControl, 170 | 0xB9BC2A50, 171 | 0x43C3, 0x41AA, 0xa0, 0x86, 172 | 0x5D, 0xB1, 0x4e, 0x18, 0x4b, 0xae 173 | ); 174 | 175 | DEFINE_GUID(sws_IID_InputSwitchControl, 176 | 0xB9BC2A50, 177 | 0x43C3, 0x41AA, 0xa0, 0x82, 178 | 0x5D, 0xB1, 0x4e, 0x18, 0x4b, 0xae 179 | ); 180 | 181 | DEFINE_GUID(sws_IID_IInputSwitchCallback, 182 | 0xB9BC2A50, 183 | 0x43C3, 0x41AA, 0xa0, 0x83, 184 | 0x5D, 0xB1, 0x4e, 0x18, 0x4b, 0xae 185 | ); 186 | 187 | typedef struct IInputSwitchCallbackUpdateData 188 | { 189 | DWORD dwID; // OK 190 | DWORD dw0; // always 0 191 | LPCWSTR pwszLangShort; // OK ("ENG") 192 | LPCWSTR pwszLang; // OK ("English (United States)") 193 | LPCWSTR pwszKbShort; // OK ("US") 194 | LPCWSTR pwszKb; // OK ("US keyboard") 195 | LPCWSTR pwszUnknown5; 196 | LPCWSTR pwszUnknown6; 197 | LPCWSTR pwszLocale; // OK ("en-US") 198 | LPCWSTR pwszUnknown8; 199 | LPCWSTR pwszUnknown9; 200 | LPCWSTR pwszUnknown10; 201 | LPCWSTR pwszUnknown11; 202 | LPCWSTR pwszUnknown12; 203 | LPCWSTR pwszUnknown13; 204 | LPCWSTR pwszUnknown14; 205 | LPCWSTR pwszUnknown15; 206 | LPCWSTR pwszUnknown16; 207 | LPCWSTR pwszUnknown17; 208 | DWORD dwUnknown18; 209 | DWORD dwUnknown19; 210 | DWORD dwNumber; // ??? 211 | } IInputSwitchCallbackUpdateData; 212 | 213 | typedef interface sws_IInputSwitchControl sws_IInputSwitchControl; 214 | 215 | typedef interface sws_IInputSwitchCallback sws_IInputSwitchCallback; 216 | 217 | typedef struct sws_IInputSwitchControlVtbl 218 | { 219 | BEGIN_INTERFACE 220 | 221 | HRESULT(STDMETHODCALLTYPE* QueryInterface)( 222 | sws_IInputSwitchControl* This, 223 | /* [in] */ REFIID riid, 224 | /* [annotation][iid_is][out] */ 225 | _COM_Outptr_ void** ppvObject); 226 | 227 | ULONG(STDMETHODCALLTYPE* AddRef)( 228 | sws_IInputSwitchControl* This); 229 | 230 | ULONG(STDMETHODCALLTYPE* Release)( 231 | sws_IInputSwitchControl* This); 232 | 233 | HRESULT(STDMETHODCALLTYPE* Init)( 234 | sws_IInputSwitchControl* This, 235 | /* [in] */ unsigned int clientType); 236 | 237 | HRESULT(STDMETHODCALLTYPE* SetCallback)( 238 | sws_IInputSwitchControl* This, 239 | /* [in] */ sws_IInputSwitchCallback* pInputSwitchCallback); 240 | 241 | END_INTERFACE 242 | } sws_IInputSwitchControlVtbl; 243 | 244 | interface sws_IInputSwitchControl 245 | { 246 | CONST_VTBL struct sws_IInputSwitchControlVtbl* lpVtbl; 247 | }; 248 | 249 | typedef struct sws_IInputSwitchCallbackVtbl 250 | { 251 | BEGIN_INTERFACE 252 | 253 | HRESULT(STDMETHODCALLTYPE* QueryInterface)( 254 | sws_IInputSwitchCallback* This, 255 | /* [in] */ REFIID riid, 256 | /* [annotation][iid_is][out] */ 257 | _COM_Outptr_ void** ppvObject); 258 | 259 | ULONG(STDMETHODCALLTYPE* AddRef)( 260 | sws_IInputSwitchCallback* This); 261 | 262 | ULONG(STDMETHODCALLTYPE* Release)( 263 | sws_IInputSwitchCallback* This); 264 | 265 | HRESULT(STDMETHODCALLTYPE* OnUpdateProfile)( 266 | sws_IInputSwitchCallback* This, 267 | /* [in] */ IInputSwitchCallbackUpdateData* ud); 268 | 269 | HRESULT(STDMETHODCALLTYPE* OnUpdateTsfFloatingFlags)( 270 | sws_IInputSwitchCallback* This); 271 | 272 | HRESULT(STDMETHODCALLTYPE* OnProfileCountChange)( 273 | sws_IInputSwitchCallback* This, 274 | /* [in] */ int a2, 275 | /* [in] */ int a3); 276 | 277 | HRESULT(STDMETHODCALLTYPE* OnShowHide)( 278 | sws_IInputSwitchCallback* This, 279 | /* [in] */ int dwShowStatus); 280 | 281 | HRESULT(STDMETHODCALLTYPE* OnImeModeItemUpdate)( 282 | sws_IInputSwitchCallback* This, 283 | /* [in] */ void* ime); 284 | 285 | HRESULT(STDMETHODCALLTYPE* OnModalitySelected)( 286 | sws_IInputSwitchCallback* This); 287 | 288 | HRESULT(STDMETHODCALLTYPE* OnContextFlagsChange)( 289 | sws_IInputSwitchCallback* This, 290 | /* [in] */ char flags); 291 | 292 | HRESULT(STDMETHODCALLTYPE* OnTouchKeyboardManualInvoke)( 293 | sws_IInputSwitchCallback* This); 294 | 295 | END_INTERFACE 296 | } sws_IInputSwitchCallbackVtbl; 297 | 298 | interface sws_IInputSwitchCallback 299 | { 300 | CONST_VTBL struct sws_IInputSwitchCallbackVtbl* lpVtbl; 301 | }; 302 | 303 | inline double sws_linear(double percent, double start, double end) 304 | { 305 | return start + (end - start) * percent; 306 | } 307 | 308 | inline double sws_easing_easeOutQuad(double p) 309 | { 310 | double m = p - 1; return 1 - m * m; 311 | } 312 | #endif 313 | -------------------------------------------------------------------------------- /SimpleWindowSwitcher/sws_dll.h: -------------------------------------------------------------------------------- 1 | #ifndef _H_SWS_DLL_ 2 | #define _H_SWS_DLL_ 3 | #include 4 | static HRESULT(*ApplyCompatResolutionQuirkingFunc)(void*, void*); 5 | __declspec(dllexport) HRESULT ApplyCompatResolutionQuirking(void* p1, void* p2) 6 | { 7 | return ApplyCompatResolutionQuirkingFunc(p1, p2); 8 | } 9 | static HRESULT(*CompatStringFunc)(void*, void*, void*, BOOL); 10 | __declspec(dllexport) HRESULT CompatString(void* p1, void* p2, void* p3, BOOL p4) 11 | { 12 | return CompatStringFunc(p1, p2, p3, p4); 13 | } 14 | static HRESULT(*CompatValueFunc)(void*, void*); 15 | __declspec(dllexport) HRESULT CompatValue(void* p1, void* p2) 16 | { 17 | return CompatValueFunc(p1, p2); 18 | } 19 | static HRESULT(*CreateDXGIFactoryFunc)(void*, void**); 20 | __declspec(dllexport) HRESULT CreateDXGIFactory(void* p1, void** p2) 21 | { 22 | return CreateDXGIFactoryFunc(p1, p2); 23 | } 24 | static HRESULT(*CreateDXGIFactory1Func)(void*, void**); 25 | __declspec(dllexport) HRESULT CreateDXGIFactory1(void* p1, void** p2) 26 | { 27 | return CreateDXGIFactory1Func(p1, p2); 28 | } 29 | static HRESULT(*CreateDXGIFactory2Func)(UINT, void*, void**); 30 | __declspec(dllexport) HRESULT CreateDXGIFactory2(UINT p1, void* p2, void** p3) 31 | { 32 | return CreateDXGIFactory2Func(p1, p2, p3); 33 | } 34 | static HRESULT(*DXGID3D10CreateDeviceFunc)(); 35 | __declspec(dllexport) HRESULT DXGID3D10CreateDevice() { 36 | return DXGID3D10CreateDeviceFunc(); 37 | } 38 | static HRESULT(*DXGID3D10CreateLayeredDeviceFunc)(); 39 | __declspec(dllexport) HRESULT DXGID3D10CreateLayeredDevice() 40 | { 41 | return DXGID3D10CreateLayeredDeviceFunc(); 42 | } 43 | static HRESULT(*DXGID3D10GetLayeredDeviceSizeFunc)(); 44 | __declspec(dllexport) HRESULT DXGID3D10GetLayeredDeviceSize() 45 | { 46 | return DXGID3D10GetLayeredDeviceSizeFunc(); 47 | } 48 | static HRESULT(*DXGID3D10RegisterLayersFunc)(); 49 | __declspec(dllexport) HRESULT DXGID3D10RegisterLayers() 50 | { 51 | return DXGID3D10RegisterLayersFunc(); 52 | } 53 | static HRESULT(*DXGIDeclareAdapterRemovalSupportFunc)(); 54 | __declspec(dllexport) HRESULT DXGIDeclareAdapterRemovalSupport() 55 | { 56 | return DXGIDeclareAdapterRemovalSupportFunc(); 57 | } 58 | static HRESULT(*DXGIDumpJournalFunc)(void*); 59 | __declspec(dllexport) HRESULT DXGIDumpJournal(void* p1) 60 | { 61 | return DXGIDumpJournalFunc(p1); 62 | } 63 | static HRESULT(*DXGIGetDebugInterface1Func)(UINT, void*, void**); 64 | __declspec(dllexport) HRESULT DXGIGetDebugInterface1(UINT p1, void* p2, void** p3) 65 | { 66 | return DXGIGetDebugInterface1Func(p1, p2, p3); 67 | } 68 | static HRESULT(*DXGIReportAdapterConfigurationFunc)(); 69 | __declspec(dllexport) HRESULT DXGIReportAdapterConfiguration() 70 | { 71 | return DXGIReportAdapterConfigurationFunc(); 72 | } 73 | static HRESULT(*PIXBeginCaptureFunc)(INT64, void*); 74 | __declspec(dllexport) HRESULT PIXBeginCapture(INT64 p1, void* p2) 75 | { 76 | return PIXBeginCaptureFunc(p1, p2); 77 | } 78 | static HRESULT(*PIXEndCaptureFunc)(); 79 | __declspec(dllexport) HRESULT PIXEndCapture() 80 | { 81 | return PIXEndCaptureFunc(); 82 | } 83 | static HRESULT(*PIXGetCaptureStateFunc)(); 84 | __declspec(dllexport) HRESULT PIXGetCaptureState() 85 | { 86 | return PIXGetCaptureStateFunc(); 87 | } 88 | static HRESULT(*SetAppCompatStringPointerFunc)(SIZE_T, void*); 89 | __declspec(dllexport) HRESULT SetAppCompatStringPointer(SIZE_T p1, void* p2) 90 | { 91 | return SetAppCompatStringPointerFunc(p1, p2); 92 | } 93 | static HRESULT(*UpdateHMDEmulationStatusFunc)(char); 94 | __declspec(dllexport) HRESULT UpdateHMDEmulationStatus(char p1) 95 | { 96 | return UpdateHMDEmulationStatusFunc(p1); 97 | } 98 | #endif 99 | -------------------------------------------------------------------------------- /SimpleWindowSwitcher/sws_error.c: -------------------------------------------------------------------------------- 1 | #include "sws_error.h" 2 | 3 | void sws_error_PrintStackTrace() 4 | { 5 | #if defined(_M_X64) 6 | DWORD machine = IMAGE_FILE_MACHINE_AMD64; 7 | #elif defined(_M_ARM64) 8 | DWORD machine = IMAGE_FILE_MACHINE_ARM64; 9 | #elif defined(_M_IX86) 10 | DWORD machine = IMAGE_FILE_MACHINE_I386; 11 | #endif 12 | HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId()); 13 | HANDLE thread = GetCurrentThread(); 14 | 15 | if (SymInitialize(process, NULL, TRUE) == FALSE) 16 | { 17 | return 0; 18 | } 19 | 20 | SymSetOptions(SYMOPT_LOAD_LINES); 21 | 22 | CONTEXT context; 23 | ZeroMemory(&context, sizeof(CONTEXT)); 24 | context.ContextFlags = CONTEXT_FULL; 25 | RtlCaptureContext(&context); 26 | 27 | STACKFRAME frame; 28 | ZeroMemory(&frame, sizeof(STACKFRAME)); 29 | #if defined(_M_X64) 30 | frame.AddrPC.Offset = context.Rip; 31 | frame.AddrPC.Mode = AddrModeFlat; 32 | frame.AddrFrame.Offset = context.Rbp; 33 | frame.AddrFrame.Mode = AddrModeFlat; 34 | frame.AddrStack.Offset = context.Rsp; 35 | frame.AddrStack.Mode = AddrModeFlat; 36 | #elif defined(_M_ARM64) 37 | frame.AddrPC.Offset = context.Pc; 38 | frame.AddrPC.Mode = AddrModeFlat; 39 | frame.AddrFrame.Offset = context.Fp; 40 | frame.AddrFrame.Mode = AddrModeFlat; 41 | frame.AddrStack.Offset = context.Sp; 42 | frame.AddrStack.Mode = AddrModeFlat; 43 | #elif defined(_M_IX86) 44 | frame.AddrPC.Offset = context.Eip; 45 | frame.AddrPC.Mode = AddrModeFlat; 46 | frame.AddrFrame.Offset = context.Ebp; 47 | frame.AddrFrame.Mode = AddrModeFlat; 48 | frame.AddrStack.Offset = context.Esp; 49 | frame.AddrStack.Mode = AddrModeFlat; 50 | #endif 51 | 52 | UINT i = 0; 53 | while (StackWalk(machine, process, thread, &frame, &context, NULL, SymFunctionTableAccess, SymGetModuleBase, NULL)) 54 | { 55 | printf("[%3d] = [0x%p] :: ", i, frame.AddrPC.Offset); 56 | 57 | UINT_PTR moduleBase = SymGetModuleBase(process, frame.AddrPC.Offset); 58 | 59 | char moduelBuff[MAX_PATH]; 60 | if (moduleBase && GetModuleFileNameA((HINSTANCE)moduleBase, moduelBuff, MAX_PATH)) 61 | { 62 | } 63 | 64 | UINT_PTR offset = 0; 65 | char symbolBuffer[sizeof(IMAGEHLP_SYMBOL) + 255]; 66 | PIMAGEHLP_SYMBOL symbol = (PIMAGEHLP_SYMBOL)symbolBuffer; 67 | symbol->SizeOfStruct = (sizeof(IMAGEHLP_SYMBOL)) + 255; 68 | symbol->MaxNameLength = 254; 69 | 70 | if (SymGetSymFromAddr(process, frame.AddrPC.Offset, &offset, symbol)) 71 | { 72 | printf("%s:", symbol->Name); 73 | } 74 | 75 | IMAGEHLP_LINE line; 76 | line.SizeOfStruct = sizeof(IMAGEHLP_LINE); 77 | 78 | DWORD offset_ln = 0; 79 | if (SymGetLineFromAddr(process, frame.AddrPC.Offset, &offset_ln, &line)) 80 | { 81 | wprintf(L"%d in file \"%s\"", line.LineNumber, line.FileName); 82 | } 83 | else 84 | { 85 | } 86 | 87 | printf("\n"); 88 | ++i; 89 | } 90 | 91 | SymCleanup(process); 92 | 93 | CloseHandle(process); 94 | } 95 | 96 | char* sws_error_NumToDescription(sws_error_t errnum, BOOL* bType) 97 | { 98 | if (bType) *bType = FALSE; 99 | char* ret = NULL; 100 | switch (errnum) 101 | { 102 | case SWS_ERROR_SUCCESS: 103 | ret = SWS_ERROR_SUCCESS_TEXT; 104 | break; 105 | case SWS_ERROR_ERROR: 106 | ret = SWS_ERROR_ERROR_TEXT; 107 | break; 108 | case SWS_ERROR_GENERIC_ERROR: 109 | ret = SWS_ERROR_GENERIC_ERROR_TEXT; 110 | break; 111 | case SWS_ERROR_NO_MEMORY: 112 | ret = SWS_ERROR_NO_MEMORY_TEXT; 113 | break; 114 | case SWS_ERROR_NOT_INITIALIZED: 115 | ret = SWS_ERROR_NOT_INITIALIZED_TEXT; 116 | break; 117 | case SWS_ERROR_LOADLIBRARY_FAILED: 118 | ret = SWS_ERROR_LOADLIBRARY_FAILED_TEXT; 119 | break; 120 | case SWS_ERROR_FUNCTION_NOT_FOUND: 121 | ret = SWS_ERROR_FUNCTION_NOT_FOUND_TEXT; 122 | break; 123 | case SWS_ERROR_UNABLE_TO_SET_DPI_AWARENESS_CONTEXT: 124 | ret = SWS_ERROR_UNABLE_TO_SET_DPI_AWARENESS_CONTEXT_TEXT; 125 | break; 126 | case SWS_ERROR_INVALID_PARAMETER: 127 | ret = SWS_ERROR_INVALID_PARAMETER_TEXT; 128 | break; 129 | case SWS_ERROR_SHELL_NOT_FOUND: 130 | ret = SWS_ERROR_SHELL_NOT_FOUND; 131 | break; 132 | case SWS_ERROR_NOERROR_JUST_PRINT_STACKTRACE: 133 | ret = SWS_ERROR_NOERROR_JUST_PRINT_STACKTRACE_TEXT; 134 | break; 135 | default: 136 | if (bType) *bType = TRUE; 137 | FormatMessageA( 138 | FORMAT_MESSAGE_ALLOCATE_BUFFER | 139 | FORMAT_MESSAGE_FROM_SYSTEM | 140 | FORMAT_MESSAGE_IGNORE_INSERTS | 141 | FORMAT_MESSAGE_MAX_WIDTH_MASK, 142 | NULL, 143 | errnum, 144 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 145 | &ret, 146 | 0, 147 | NULL 148 | ); 149 | break; 150 | } 151 | return ret; 152 | } 153 | 154 | sws_error_t sws_error_Report(sws_error_t errnum, void* data) 155 | { 156 | if (errnum == SWS_ERROR_SUCCESS) 157 | { 158 | return errnum; 159 | } 160 | char* errdesc = NULL; 161 | _lock_file(stdout); 162 | printf("====================================\nAn error occured in the application.\nError number: 0x%x\n", errnum); 163 | BOOL bShouldFree = FALSE; 164 | if (errdesc = sws_error_NumToDescription(errnum, &bShouldFree)) 165 | { 166 | printf("Description: %s\n", errdesc); 167 | if (bShouldFree) LocalFree(errdesc); 168 | } 169 | else 170 | { 171 | puts("."); 172 | } 173 | if (data) 174 | { 175 | printf("Additional data: 0x%p\n", data); 176 | } 177 | puts("Here is the stack trace:"); 178 | sws_error_PrintStackTrace(); 179 | puts("===================================="); 180 | _unlock_file(stdout); 181 | return errnum; 182 | } -------------------------------------------------------------------------------- /SimpleWindowSwitcher/sws_error.h: -------------------------------------------------------------------------------- 1 | #ifndef _H_SWS_ERROR_H_ 2 | #define _H_SWS_ERROR_H_ 3 | #include 4 | #include 5 | #define DBGHELP_TRANSLATE_TCHAR 6 | #include 7 | #pragma comment(lib, "Dbghelp.lib") 8 | 9 | // References: 10 | // https://github.com/rioki/rex/blob/master/rex/dbg.h#L100 11 | 12 | typedef HRESULT sws_error_t; 13 | 14 | #define SWS_ERROR_SUCCESS S_OK 15 | #define SWS_ERROR_SUCCESS_TEXT "The operation completed successfully" 16 | #define SWS_ERROR_ERROR S_FALSE 17 | #define SWS_ERROR_ERROR_TEXT "General failure" 18 | #define SWS_ERROR_GENERIC_ERROR 0xA0010001 19 | #define SWS_ERROR_GENERIC_ERROR_TEXT "A generic error has occured" 20 | #define SWS_ERROR_NO_MEMORY 0xA0010002 21 | #define SWS_ERROR_NO_MEMORY_TEXT "Insufficient memory. Please close some applications and try again" 22 | #define SWS_ERROR_NOT_INITIALIZED 0xA0010003 23 | #define SWS_ERROR_NOT_INITIALIZED_TEXT "Functionality is not initialized" 24 | #define SWS_ERROR_LOADLIBRARY_FAILED 0xA0010004 25 | #define SWS_ERROR_LOADLIBRARY_FAILED_TEXT "The requested library is not available" 26 | #define SWS_ERROR_FUNCTION_NOT_FOUND 0xA0010005 27 | #define SWS_ERROR_FUNCTION_NOT_FOUND_TEXT "The requested procedure was not found" 28 | #define SWS_ERROR_UNABLE_TO_SET_DPI_AWARENESS_CONTEXT 0xA0010006 29 | #define SWS_ERROR_UNABLE_TO_SET_DPI_AWARENESS_CONTEXT_TEXT "Unable to set the requested DPI awareness context" 30 | #define SWS_ERROR_INVALID_PARAMETER 0xA0010007 31 | #define SWS_ERROR_INVALID_PARAMETER_TEXT "One or more of the parameters supplied is invalid" 32 | #define SWS_ERROR_SHELL_NOT_FOUND 0xA0010008 33 | #define SWS_ERROR_SHELL_NOT_FOUND_TEXT "A compatible shell application is not available" 34 | #define SWS_ERROR_NOERROR_JUST_PRINT_STACKTRACE 0xA0010009 35 | #define SWS_ERROR_NOERROR_JUST_PRINT_STACKTRACE_TEXT "THIS IS NOT A BUG, A DELIBERATE STACK TRACE REQUEST HAS BEEN MADE" 36 | #define SWS_ERROR_APPRESOLVER_NOT_AVAILABLE 0xA001000A 37 | #define SWS_ERROR_APPRESOLVER_NOT_AVAILABLE_TEXT "Unable to initialize an instance of IAppResolver8" 38 | 39 | 40 | #ifdef __cplusplus 41 | extern "C" 42 | { 43 | #endif 44 | void sws_error_PrintStackTrace(); 45 | 46 | char* sws_error_NumToDescription(sws_error_t errnum, BOOL* bType); 47 | 48 | sws_error_t sws_error_Report(sws_error_t errnum, void* data); 49 | 50 | inline sws_error_t sws_error_GetFromInternalError(HRESULT hResult) 51 | { 52 | return hResult; 53 | } 54 | 55 | inline sws_error_t sws_error_GetFromHRESULT(HRESULT hResult) 56 | { 57 | return hResult; 58 | } 59 | 60 | inline sws_error_t sws_error_GetFromWin32Error(DWORD win32err) 61 | { 62 | return HRESULT_FROM_WIN32(win32err); 63 | } 64 | 65 | inline sws_error_t sws_error_GetFromErrno(errno_t err) 66 | { 67 | return sws_error_GetFromInternalError(err); 68 | } 69 | 70 | inline sws_error_t sws_error_GetFromGdiplusStatus(int err) 71 | { 72 | return err; 73 | } 74 | #ifdef __cplusplus 75 | } 76 | #endif 77 | #endif 78 | -------------------------------------------------------------------------------- /SimpleWindowSwitcher/sws_tshwnd.c: -------------------------------------------------------------------------------- 1 | #include "sws_tshwnd.h" 2 | 3 | void sws_tshwnd_ModifyTimestamp(sws_tshwnd* _this, FILETIME ft) 4 | { 5 | _this->ft = ft; 6 | } 7 | 8 | void sws_tshwnd_UpdateTimestamp(sws_tshwnd* _this) 9 | { 10 | GetSystemTimeAsFileTime(&(_this->ft)); 11 | } 12 | 13 | int sws_tshwnd_CompareTimestamp(sws_tshwnd* p1, sws_tshwnd* p2, LPARAM flags) 14 | { 15 | if (flags & SWS_SORT_DESCENDING) 16 | { 17 | return CompareFileTime(&(p2->ft), &(p1->ft)); 18 | } 19 | return CompareFileTime(&(p1->ft), &(p2->ft)); 20 | } 21 | 22 | int sws_tshwnd_CompareHWND(sws_tshwnd* p1, sws_tshwnd* p2) 23 | { 24 | return !(p1 && p2 && p1->hWnd == p2->hWnd); 25 | } 26 | 27 | BOOL sws_tshwnd_GetFlashState(sws_tshwnd* _this) 28 | { 29 | return _this->bFlash; 30 | } 31 | 32 | void sws_tshwnd_SetFlashState(sws_tshwnd* _this, BOOL bFlash) 33 | { 34 | _this->bFlash = bFlash; 35 | } 36 | 37 | sws_error_t sws_tshwnd_Initialize(sws_tshwnd* _this, HWND hWnd) 38 | { 39 | sws_error_t rv = SWS_ERROR_SUCCESS; 40 | 41 | if (!rv && _this) 42 | { 43 | _this->hWnd = hWnd; 44 | GetSystemTimeAsFileTime(&(_this->ft)); 45 | _this->bFlash = FALSE; 46 | _this->cbFlashAnimationState = 0; 47 | _this->dwFlashAnimationState = 0; 48 | } 49 | 50 | return rv; 51 | } 52 | -------------------------------------------------------------------------------- /SimpleWindowSwitcher/sws_tshwnd.h: -------------------------------------------------------------------------------- 1 | #ifndef _H_SWS_TSHWND_ 2 | #define _H_SWS_TSHWND_ 3 | #include 4 | #include "sws_def.h" 5 | #include "sws_error.h" 6 | 7 | typedef struct _sws_tshwnd 8 | { 9 | HWND hWnd; 10 | FILETIME ft; 11 | BOOL bFlash; 12 | double cbFlashAnimationState; 13 | DWORD dwFlashAnimationState; 14 | } sws_tshwnd; 15 | 16 | void sws_tshwnd_ModifyTimestamp(sws_tshwnd* _this, FILETIME ft); 17 | 18 | void sws_tshwnd_UpdateTimestamp(sws_tshwnd* _this); 19 | 20 | int sws_tshwnd_CompareTimestamp(sws_tshwnd* p1, sws_tshwnd* p2, LPARAM flags); 21 | 22 | int sws_tshwnd_CompareHWND(sws_tshwnd* p1, sws_tshwnd* p2); 23 | 24 | BOOL sws_tshwnd_GetFlashState(sws_tshwnd* _this); 25 | 26 | void sws_tshwnd_SetFlashState(sws_tshwnd* _this, BOOL bFlash); 27 | 28 | sws_error_t sws_tshwnd_Initialize(sws_tshwnd* _this, HWND hWnd); 29 | #endif -------------------------------------------------------------------------------- /SimpleWindowSwitcher/sws_utility.h: -------------------------------------------------------------------------------- 1 | #ifndef _H_SWS_UTILITY_H_ 2 | #define _H_SWS_UTILITY_H_ 3 | #include 4 | inline long long sws_milliseconds_now() { 5 | LARGE_INTEGER s_frequency; 6 | BOOL s_use_qpc = QueryPerformanceFrequency(&s_frequency); 7 | if (s_use_qpc) { 8 | LARGE_INTEGER now; 9 | QueryPerformanceCounter(&now); 10 | return (1000LL * now.QuadPart) / s_frequency.QuadPart; 11 | } 12 | else { 13 | return GetTickCount(); 14 | } 15 | } 16 | 17 | // https://stackoverflow.com/questions/13397571/precise-thread-sleep-needed-max-1ms-error 18 | inline BOOLEAN sws_nanosleep(LONGLONG ns) { 19 | /* Declarations */ 20 | HANDLE timer; /* Timer handle */ 21 | LARGE_INTEGER li; /* Time defintion */ 22 | /* Create timer */ 23 | if (!(timer = CreateWaitableTimer(NULL, TRUE, NULL))) 24 | return FALSE; 25 | /* Set timer properties */ 26 | li.QuadPart = -ns; 27 | if (!SetWaitableTimer(timer, &li, 0, NULL, NULL, FALSE)) { 28 | CloseHandle(timer); 29 | return FALSE; 30 | } 31 | /* Start & wait for timer */ 32 | WaitForSingleObject(timer, INFINITE); 33 | /* Clean resources */ 34 | CloseHandle(timer); 35 | /* Slept without problems */ 36 | return TRUE; 37 | } 38 | #endif -------------------------------------------------------------------------------- /SimpleWindowSwitcher/sws_vector.c: -------------------------------------------------------------------------------- 1 | #include "sws_vector.h" 2 | 3 | sws_error_t sws_vector_PushBack(sws_vector* _this, void* pElement) 4 | { 5 | sws_error_t rv = SWS_ERROR_SUCCESS; 6 | 7 | if (!rv) 8 | { 9 | if (_this->cbCapacity == 0 || _this->cbElementSize == 0) 10 | { 11 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_NO_MEMORY), NULL); 12 | } 13 | } 14 | if (!rv) 15 | { 16 | if (_this->cbSize >= _this->cbCapacity) 17 | { 18 | void* prev = _this->pList; 19 | _this->pList = realloc(_this->pList, (uintptr_t)_this->cbElementSize * (uintptr_t)((uintptr_t)_this->cbCapacity + SWS_VECTOR_CAPACITY)); 20 | if (!_this->pList) 21 | { 22 | free(prev); 23 | _this->cbElementSize = 0; 24 | _this->cbCapacity = 0; 25 | _this->cbSize = 0; 26 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_NO_MEMORY), NULL); 27 | } 28 | else 29 | { 30 | _this->cbCapacity = _this->cbCapacity + SWS_VECTOR_CAPACITY; 31 | } 32 | } 33 | } 34 | if (!rv) 35 | { 36 | memcpy((uintptr_t)_this->pList + (uintptr_t)_this->cbSize * (uintptr_t)_this->cbElementSize, pElement, _this->cbElementSize); 37 | _this->cbSize++; 38 | } 39 | 40 | return rv; 41 | } 42 | 43 | void sws_vector_Clear(sws_vector* _this) 44 | { 45 | if (_this) 46 | { 47 | #if defined(DEBUG) | defined(_DEBUG) 48 | printf("[sws] >> vector::free: %p\n", _this->pList); 49 | #endif 50 | free(_this->pList); 51 | memset(_this, 0, sizeof(sws_vector)); 52 | } 53 | } 54 | 55 | sws_error_t sws_vector_Initialize(sws_vector* _this, unsigned int cbElementSize) 56 | { 57 | sws_error_t rv = SWS_ERROR_SUCCESS; 58 | 59 | if (!rv) 60 | { 61 | if (!_this) 62 | { 63 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_NO_MEMORY), NULL); 64 | } 65 | } 66 | if (!rv) 67 | { 68 | _this->pList = calloc(SWS_VECTOR_CAPACITY, cbElementSize); 69 | if (!_this->pList) 70 | { 71 | rv = sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_NO_MEMORY), NULL); 72 | } 73 | #if defined(DEBUG) | defined(_DEBUG) 74 | printf("[sws] >> vector::alloc: %p\n", _this->pList); 75 | //sws_error_Report(sws_error_GetFromInternalError(SWS_ERROR_NOERROR_JUST_PRINT_STACKTRACE), NULL); 76 | #endif 77 | _this->cbElementSize = cbElementSize; 78 | _this->cbCapacity = SWS_VECTOR_CAPACITY; 79 | _this->cbSize = 0; 80 | } 81 | 82 | return rv; 83 | } -------------------------------------------------------------------------------- /SimpleWindowSwitcher/sws_vector.h: -------------------------------------------------------------------------------- 1 | #ifndef _H_SWS_VECTOR_H_ 2 | #define _H_SWS_VECTOR_H_ 3 | #include 4 | #include 5 | #include "sws_def.h" 6 | #include "sws_error.h" 7 | 8 | typedef struct _sws_vector 9 | { 10 | void* pList; 11 | int cbSize; 12 | int cbCapacity; 13 | int cbElementSize; 14 | } sws_vector; 15 | 16 | sws_error_t sws_vector_PushBack(sws_vector* _this, void* pElement); 17 | 18 | void sws_vector_Clear(sws_vector* _this); 19 | 20 | sws_error_t sws_vector_Initialize(sws_vector* _this, unsigned int cbElementSize); 21 | #endif 22 | -------------------------------------------------------------------------------- /SimpleWindowSwitcher/sws_window.c: -------------------------------------------------------------------------------- 1 | #include "sws_window.h" 2 | 3 | extern NTSTATUS NTAPI NtQueryInformationProcess( 4 | _In_ HANDLE ProcessHandle, 5 | _In_ DWORD ProcessInformationClass, 6 | _Out_writes_bytes_(ProcessInformationLength) PVOID ProcessInformation, 7 | _In_ ULONG ProcessInformationLength, 8 | _Out_opt_ PULONG ReturnLength); 9 | #define ProcessConsoleHostProcess 49 10 | 11 | void sws_window_Clear(sws_window* _this) 12 | { 13 | if (_this->hWnd) 14 | { 15 | _this->hWnd = NULL; 16 | _this->dwProcessId = 0; 17 | ZeroMemory(_this->wszPath, MAX_PATH); 18 | _this->bIsApplicationFrameHost = FALSE; 19 | _this->pNextWindow = NULL; 20 | } 21 | if (_this->wszAUMID) 22 | { 23 | CoTaskMemFree(_this->wszAUMID); 24 | _this->wszAUMID = NULL; 25 | } 26 | } 27 | 28 | sws_error_t sws_window_Initialize(sws_window* _this, HWND hWnd) 29 | { 30 | sws_error_t rv = SWS_ERROR_SUCCESS; 31 | 32 | if (!rv) 33 | { 34 | ZeroMemory(_this->wszPath, MAX_PATH); 35 | _this->hWnd = hWnd; 36 | } 37 | if (!rv) 38 | { 39 | HWND hWndOfInterest = _sws_HungWindowFromGhostWindow(hWnd); 40 | if (!hWndOfInterest) hWndOfInterest = hWnd; 41 | if (!GetWindowThreadProcessId(hWndOfInterest, &(_this->dwProcessId))) 42 | { 43 | rv = sws_error_GetFromWin32Error(GetLastError()); 44 | } 45 | } 46 | if (!rv) 47 | { 48 | HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, _this->dwProcessId); 49 | if (hProcess) 50 | { 51 | GetModuleFileNameExW(hProcess, NULL, _this->wszPath, MAX_PATH); 52 | //NtQueryInformationProcess(hProcess, ProcessConsoleHostProcess, &(_this->dwConhostPID), sizeof(UINT_PTR), NULL); 53 | //_this->dwConhostPID--; 54 | //wprintf(L"%d - %s\n", _this->dwConhostPID, _this->wszPath); 55 | CloseHandle(hProcess); 56 | } 57 | } 58 | if (!rv) 59 | { 60 | /* 61 | wchar_t path[MAX_PATH]; 62 | ZeroMemory(path, MAX_PATH); 63 | memcpy(path, _this->wszPath, MAX_PATH); 64 | wchar_t syspath[MAX_PATH]; 65 | ZeroMemory(syspath, MAX_PATH); 66 | GetSystemDirectoryW(syspath, MAX_PATH); 67 | wcscat_s(syspath, MAX_PATH, L"\\ApplicationFrameHost.exe"); 68 | //wprintf(L"%s %s\n", path, syspath); 69 | _this->bIsApplicationFrameHost = !_wcsicmp(path, syspath); 70 | */ 71 | _this->bIsApplicationFrameHost = sws_IsShellFrameWindow(hWnd); 72 | //_this->bIsApplicationFrameHost = sws_WindowHelpers_IsWindowUWP(hWnd); 73 | _this->tshWnd = NULL; 74 | _this->pNextWindow = NULL; 75 | } 76 | if (!rv) 77 | { 78 | _this->wszAUMID = sws_WindowHelpers_GetAUMIDForHWND(_this->hWnd); 79 | } 80 | return rv; 81 | } 82 | -------------------------------------------------------------------------------- /SimpleWindowSwitcher/sws_window.h: -------------------------------------------------------------------------------- 1 | #ifndef _H_SWS_WINDOW_ 2 | #define _H_SWS_WINDOW_ 3 | #include 4 | #include 5 | #include 6 | #include "sws_WindowHelpers.h" 7 | #include "sws_error.h" 8 | #include "sws_tshwnd.h" 9 | 10 | typedef struct _sws_window 11 | { 12 | HWND hWnd; 13 | DWORD dwProcessId; 14 | wchar_t wszPath[MAX_PATH]; 15 | BOOL bIsApplicationFrameHost; 16 | sws_tshwnd* tshWnd; 17 | wchar_t* wszAUMID; 18 | struct _sws_window* pNextWindow; 19 | } sws_window; 20 | 21 | void sws_window_Clear(sws_window* _this); 22 | 23 | sws_error_t sws_window_Initialize(sws_window* _this, HWND hWnd); 24 | #endif --------------------------------------------------------------------------------