Jump to content

Recommended Posts

Posted (edited)

This script is evolving quite rapidly, therefore changes will be ongoing. This is inspired by the Great @argumentum and Win11myOwnBorderColor as much of the framework comes from that script.

Note: This is for Windows 11 build 22621+

Using that script as a basis, I have since:

  • Added separate variable for title bar color
  • Added the ability to change window corner preference (eg. rounded corners, square corners, etc.)
  • Added the ability to extend the frame into the client area
  • Added ability to apply backdrop material (eg. Mica, Acrylic, Mica Alt (Tabbed), etc.)
  • Fixed child windows not having colors applied (changed to SetWinEventHook)
  • TO DO list at top of script of stuff that I still need to add

At the moment, the script is running without Admin, therefore will only apply to user-mode apps. If you want to apply to all apps, remove the comment before #RequireAdmin. Eventually, I am going to add the ability to create a scheduled task (like Win11myOwnBorderColor already does).

I already have an AutoIt tray tool, so I will probably integrate this into my other tray tool. I may keep it as a separate executable which would then be run as a scheduled task.

 

Problems:

If you run into any problems with any apps, you can press Esc key to close DwmColorBlurMica and any apps that were not happy with it, simply restart them. I have excluded some apps in the script, such as Explorer and Start menu. But I still need to work on an exclusion list (and maybe inclusion list) that is either in the INI file or other file. I would like to be able to eventually load those into an array at some point.

 

Hotkey WinKey + Esc will exit the program and undo all changes made except DwmExtendFrameIntoClientArea.

 

Features:

  • Change border color to any color
  • Change title bar color to any color
  • Enable dark mode app settings (particularly dark mode title bar)
  • Apply backdrop material (eg. Mica, Acrylic, Mica Alt (Tabbed), etc.) to the title bar
  • Applies settings to all currently running windows and new windows
  • Extend title bar color and materials to client area *

* Please note that the extending of color and materials to client area (DwmExtendFrameIntoClientArea) works best with pure dark themes. For example, Rectify11's Black (Mica) theme. Also, some apps simply don't work well with it. It is a feature that really should be Opt-in per process which is something that I still need to implement.

The possibilities (especially when mixing some of these options) are almost endless. The screenshots below only show a small amount of what is possible.

 

Screenshots:

Spoiler

image.png.fc19d6a5ff7c44d1fc6b79b0c50d83bb.png  image.png.ca6b0289a3df429f66948d63926d22a5.png

image.png.d9279a55fa63f24df4d3789ec09dc6e1.png  image.png.d4d2f5ff2abb3d4465a295879c2881b3.png

image.png.82a1c2ac32cd7049cab3e56470fe73d7.png  image.png.ccd61dfde7370a5a356d5e246e9eb870.png

image.png.d799d6e135dc40c128cb9c813a857b12.png  image.png.33f79dc844f888917a8d64a82ed03765.png

 

Changelog:

Spoiler

Version 0.7

  • Improved the overall speed of the event hook
  • Improvements to the File Explorer hook
    • Separate handling for classic and modern File Explorer
    • Added ExplorerPaintDelay option (default 200ms) for modern File Explorer
    • ExplorerPaintDelay is only used for DwmExtendFrameIntoClientArea
    • Improvements for Blur Behind with modern File Explorer

Version 0.6

  • reworked entire event hook
  • significantly less CPU usage (almost nothing)
  • identified WinGetTitle as causing lag and CPU usage
    • WinGetTitle would freeze the script entirely under rare circumstances
    • switched to _WinAPI_GetWindowText but it was problematic
    • modified/fixed function with _WinAPI_GetWindowTextMod
    • CPU usage is dramatically lower as a bonus
  • added wildcards for process name and class name for inclusions/exclusions
    • wildcards can only be at start and/or end (not in the middle)

Version 0.5

  • Dramatic performance improvement after switching from _ProcessGetName to _WinAPI_GetWindowFileName

Version 0.4

  • File Explorer hook now supports modern and classic File Explorer
  • Added option to use Accent Color for border color and/or title bar color
    • Options BorderColorAccent and TitleBarColorAccent added to config file
  • Added more classes to global exclusion list to prevent issues
  • Performance improvements
    • Made separate Case for File Explorer
    • Filtering File Explorer by class name (_WinAPI_GetClassName) instead of process name

Version 0.3

  • significant hook performance and efficiency improvements
  • added hWnd (handle/control) cache
  • Explorer exception from cache because is needs pampering always
  • added more classes to global exclusions

Version 0.2

  • added Blur Behind
  • added Blur Behind opt-in by Class or Process in config
  • Blur Behind also removes cleanly without needing to restart process
  • improved Explorer hook
  • added some rule prioritization (ensures no incompatible settings are used together)

Version 0.1

  • added global exclusions by class name
  • added DwmExtendFrameIntoClientArea inclusions by class name

Version 0.0.10

  • custom system tray tool
  • created new Fluent icon
  • using TaskScheduler UDF (by water) now
  • option to install/remove scheduled task
  • run as Admin to have option to install Admin level scheduled task
  • option to Refresh Config Changes (apply new changes from config file)
  • figured out how to remove DwmExtendFrameIntoClientArea properly
  • added command line args for:
    • "addtask"
    • "removetask"
    • "stoptask"
    • "starttask"

Version 0.0.4

  • changed hotkey to WinKey+Esc
  • hotkey will exit program and undo all changes made except DwmExtendFrameIntoClientArea
  • some changes suggested by argumentum

Version 0.0.3

  • added process exclusion list to INI to exclude processes from all functions
  • created separate function for DwmExtendFrameIntoClientArea to make it Opt-in only
  • changed events to $EVENT_SYSTEM_FOREGROUND, $EVENT_OBJECT_FOCUS, $EVENT_OBJECT_CREATE
  • updated INI file with process exclusion/inclusion entries

Version 0.0.2

  • removed Explorer from exclusion list (we can have fun with Explorer now!)
  • added exclusion rule for "Program Manager" window name so that desktop isn't modified
  • added exclusion rule for blank window names which reduced CPU significantly
  • added events ($EVENT_OBJECT_SHOW, $EVENT_OBJECT_FOCUS, $EVENT_SYSTEM_FOREGROUND)

 

Current script version: 0.7

 

 

DwmColorBlurMica-0.7-Source.7z

Edited by WildByDesign
Added version 0.7
Posted

Question for anybody:

If I am adding a process exclusion list and a process inclusion list to a configuration file, should I continue using the INI file or would it be better to switch to XML entirely?

I am hoping to have only one single configuration file to keep things portable. I will likely bring the process exclusion/inclusion lists into two separate arrays.

But what I don’t know is whether I should stick with the INI file or switch to XML. Any suggestions would be appreciated.

Posted

Use whatever you feel comfortable with. INI, XML, JSON, ...SQLite, ...anything that you can write to and read from is good.
As far as portability, carrying 2 files or 20, is portable if it does not need a declaration. Say you keep a folder structure like, .\settings\thisfile and .\settings\thatFile, is all "portable" by definition.
IniRead(), IniWrite(), Ini* whatever is internal to AutoIt3. SQLite would require an external file ( the DLL ) but is all portable, as far as portability goes.
And you are not switching to XML given that there is no prior release version while you explore what is best for your project so, use XML if you feel comfortable with it. That's my view.

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Posted

..also, 
@AppDataDir Path to current user's Roaming Application Data 
@LocalAppDataDir Path to current user's Local Application Data 

So I personally use those to keep stuff at in the format of  @LocalAppDataDir & "\MyInitials(argg)\Project(awesomeness)\FilesThatAreNotExecutablesAreHere"

And I call that portable too, just because I don't use the registry. My code knows where information is centralized at. 

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Posted

I think that INI would be easier for user to modify. I just didn’t know if there were limitations to Value size.

For example, I imagine there could be something like 10-30 process names in exclusion list or inclusion list. Nothing significant. I’m not sure yet if INI can hold that in one value or not. But I will test and find out.

I was thinking of using iniread func to pull in as a string and use StringSplit func to create the array.

Posted (edited)
16 minutes ago, WildByDesign said:

I imagine there could be something like 10-30 process names in exclusion list or inclusion list.

..that, ...that will take some exploring. Maybe you could exclude a class naming convention given that a programing platform is the one that will not work well with M$ definitions or those are newer or something.
In regards to users editing a file..., yes and no. The user should have a GUI that handles that. We are coders ( this is Sparta kind of thing ) and "users" are not coders so the likelihood of a regular user editing a file is remote, other than power users and even then, a GUI is a better place.

Because lazy coders ( me most of the time ) say "set this in the ini file", does not mean that that, is how users should use software :) 

Edited by argumentum
added link for the LOLs

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Posted (edited)

Just for the heck of it, this would be "a" config file

This is a configuration file, because that's the way I thought of it =D
The only the lines that start with ">" are the field
and the value/data of the field is after the first "=" from the left
Therefore
>MyNameIs=argumentum
is a declaration, anything else in this file
is just a comment.
The more the comments, the better =)

and the reader func

#include <Debug.au3>

_DebugArrayDisplay(ConfigReadingCRLF("MyFile.config.txt"), "Entry count: " & @extended) ; or watever extension we invent
Func ConfigReadingCRLF($sFile)
    Local $aArray = FileReadToArray($sFile)
    Local $iIndex = 0, $aReturn[UBound($aArray) + 1][2]
    For $n = 0 To UBound($aArray) - 1
        If StringLeft($aArray[$n], 1) <> ">" Then ContinueLoop
        If Not StringInStr($aArray[$n], "=") Then ContinueLoop
        $iIndex += 1
        $aReturn[$iIndex][0] = StringStripWS(StringTrimLeft(StringLeft($aArray[$n], StringInStr($aArray[$n], "=", 0, 1) - 1), 1), 3)
        $aReturn[$iIndex][1] = StringStripWS(StringTrimLeft($aArray[$n], StringInStr($aArray[$n], "=", 0, 1)), 3)
    Next
    ReDim $aReturn[$iIndex + 1][2]
    $aReturn[0][0] = $iIndex
    $aReturn[0][1] = "ConfigFile"
    Return SetError(0, $iIndex, $aReturn)
EndFunc

that may look strange but who cares !, is functional. Go figure, in the future coders will use this most awesome brainstorming in their code and it'll become a standard 🤪

Joke aside, use a standard or another. But know that you are not limited by standards. Admittedly, a standard is better because is known. But that is all**.

**There is this thing called computer "science" and those in it explore efficiencies and inefficiencies but I never cared much for that, unless I had to.

Spoiler

...and the ADHD took me to The Making of "She Blinded Me With Science" :D

 

Edited by argumentum
English

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Posted
1 hour ago, argumentum said:

that may look strange but who cares !, is functional. Go figure, in the future coders will use this most awesome brainstorming in their code and it'll become a standard 🤪

Thank you for sharing this. I will go over this later tonight and figure out the config, arrays, etc. But for now, I am worn out after smoothing out the File Explorer stuff (version 0.0.2). It's all good now but I need a break for a few hours. :)

Posted

I've updated the first post with version 0.0.3.

The process exclusion list is to exclude specific processes from all functions. The process inclusion list is only for DwmExtendFrameIntoClientArea, effectively making that function Opt-in only.

  • added process exclusion list to INI to exclude processes from all functions
  • created separate function for DwmExtendFrameIntoClientArea to make it Opt-in only
  • changed events to $EVENT_SYSTEM_FOREGROUND, $EVENT_OBJECT_FOCUS, $EVENT_OBJECT_CREATE
    • this was particularly needed for Explorer

You will need the updated INI file as well.

Posted

I just updated the first post with version 0.0.4.

With this version, I changed the hotkey to WinKey+Esc as suggested by @argumentum.

Now when you use the hotkey, it will undo the changes made:

  • rounded corners will reset to default
  • title bar will reset to default color
  • border color will reset to default
  • title bar backdrop material will also reset to default

Note: The only thing that I don't think can be reset is the DwmExtendFrameIntoClientArea attribute.

Posted

I've updated the first post with version 0.0.10 that has a lot of changes. Check first post for all changlog details.

Mainly, I've created a new Fluent icon to go along with the new system tray tool. Almost everything can be done from the tray tool except the actually modifying of settings. You still have to modify INI file to make changes. But the tray tool has a new option "Refresh Config Changes" which will remove colors applied based on current settings, reload INI file to pick up new changes and apply those new changes.

I finally figured out how to remove DwmExtendFrameIntoClientArea once it has been applied. So that get removed cleanly on Exit now and also when changing config options. For example, let's say you have cmd.exe in ExtendFrameIntoClientArea process inclusions list. You have DwmExtendFrameIntoClientArea applied making Command Prompt all fancy. Then you remove cmd.exe from ExtendFrameIntoClientArea in config file, select "Refresh Config Changes" in tray menu, and Command Prompt is clean from fancy stuff but has whatever other color changes you wanted from config.

Everything is pretty smooth now. I still probably need GUI for modifying INI config file to make it easy to make changes, but I don't enjoy making the GUI so much.

icon.png

Posted

Here is SciTE Editor in full Mica:

Spoiler

image.png.27283cd465f6e2437356f53a88f8627f.png

It obviously needs some new icons for other color modes. But we can color the entire SciTE window in any color possible.

I used the SciTE dark theme from @ioa747 and modified it a bit to ensure that any background part was #000000 for full effect.

Posted

The hooking engine is getting really versatile now. I just updated the first post with version 0.1.0.

I've added added global exclusions by class name and added DwmExtendFrameIntoClientArea inclusions by class name. Both can be accessed and modified in the INI config file now for more control over everything.

Posted (edited)

The first post has been updated with version 0.2. I've added Blur Behind which is Opt-in by class or process and those options are added to the config file. Blur Behind also removes cleanly. I also added some rule prioritization to ensure that no incompatible settings are used at the same time.

All settings when applied and removed are all done cleanly without the need to restart the affected processes which is quite nice.

EDIT: I also switched to OnEvent mode for slightly improved performance.

Edited by WildByDesign
Posted

I just updated the first post with version 0.3. There are some major performance improvements in the hook that I would like to share.

First, I noticed that one program (System Information / msinfo32.exe) would almost always lock up the entire script and it would not continue until I close that app. Then the script would continue. So I dug into the _WinEventProc function to see what was happening. ConsoleWrite was extremely beneficial.

For most apps, I noticed that the same hWnd would repetitively get hammered through the functions. There would be dozens in a row. With System Information (msinfo32.exe), each time it launched there would be close to 180 duplicate hWnd's getting hammered through and causing the script to stall.

So in version 0.3, the function looks like this now:

Spoiler
Func _WinEventProc($hHook, $iEvent, $hWnd, $iObjectID, $iChildID, $iEventThread, $imsEventTime)
    Local Static $hWndLast
    Switch $iEvent
        Case $EVENT_OBJECT_CREATE, $EVENT_SYSTEM_FOREGROUND, $EVENT_OBJECT_FOCUS
            If $hWnd<>$hWndLast Then
                $hWndLast = $hWnd
            ElseIf $hWnd=$hWndLast Then
                ContinueCase
            EndIf
            
            Local $sActiveWindow = WinGetTitle($hWnd)

            ; exclude coloring windows with a blank window title
            If $sActiveWindow = "" Then ContinueCase

            ; explorer needs extra love and attention
            If StringInStr($sActiveWindow, "File Explorer") Then
                SetBorderColor_apply2this($hWnd, $dBorderColor)
                ContinueCase
            EndIf

            $aMapKeys = MapKeys($mMap)
            For $vKey In $aMapKeys
                If $hWnd = $mMap[$vKey] Then
                ConsoleWrite("Already exists on map: " & $hWnd & " (skipping)" & @CRLF) 
                ContinueCase
                EndIf
            Next

            SetBorderColor_apply2this($hWnd, $dBorderColor)

            ; add hWnd to Map
            $mMap[$hWnd] = $hWnd
    EndSwitch
EndFunc

 

You'll notice at the very top of the function, I discard duplicate hWnd's from continuing through the rest of the function:

If $hWnd<>$hWndLast Then
    $hWndLast = $hWnd
ElseIf $hWnd=$hWndLast Then
    ContinueCase
EndIf

That alone gave the majority of performance gains and stopped System Information from locking up the script. I was able to confirm everything with simple ConsoleWrite to see what was making it through. This only stopped duplicate hWnd's in succession. But yet still significant and solved the problem.

I decided to slim down the _WinEventProc further and try for more gains. I decided to use AutoIt Maps to cache the hWnd's that made it through so that any future duplicate hWnd's would not go through the other functions and be discarded. In the same func:

$aMapKeys = MapKeys($mMap)
For $vKey In $aMapKeys
    If $hWnd = $mMap[$vKey] Then
    ConsoleWrite("Already exists on map: " & $hWnd & " (skipping)" & @CRLF) 
    ContinueCase
    EndIf
Next

SetBorderColor_apply2this($hWnd, $dBorderColor)

; add hWnd to Map
$mMap[$hWnd] = $hWnd

Anytime you restore and app or bring back focus, it would want to apply colors again when not needed. So this prevents the same hWnd's from going through all of the functions again.

You will notice in the full func (Spoiler above) that File Explorer is completely ignored and bypasses the storing of hWnd. This is because everytime File Explorer comes back in focus, it loses any colors applied to it over and over again. So File Explorer is the only app which does need the hWnd to go through the coloring functions anytime it gains focus. MFE (MicaForEveryone) loses coloring and doesn't gain it back. DwmColorBlurMica (at present) has a much better hook for File Explorer in comparison.

At the moment, I am using a simple AdlibRegister to run every 1 hour to clear the hWnd (Map) cache to prevent the cache from becoming too big. So each hour, the hWnd cache would build up again but the CPU usage is next to nothing. I'm not sure if I should have it run every hour to clear the cache or maybe every 24 hours. But I could probably add it to the config file so that the user can change it.

@argumentum has suggested a beautiful method for clearing the hWnd (Map) cache to me. But I don't fully understand it. I try to my best to put time into things so that I can understand how they work before I implement them. Also, I am very hesitant to add anything else to the _WinEventProc function because anything in there is going to get hammered sometimes dozens of times per second. But also since my simple AdlibRegister function is completely negligible on CPU even while user is using the PC at the same time, it is unnoticeable. Therefore, I think simple is ok in this instance.

Also, previously I was running WinGetProcess,  _WinAPI_GetClassName and _ProcessGetName in several of the coloring functions for the purpose of the process exclusions. Now, I only run those once and pass the class name and process name to any of the coloring functions that need it.

So performance improvements and efficiency all around in this release.

Posted

The first post has been updated with version 0.4 with updated changelog.

I finally implemented support for applying system accent color to the borders and/or title bars. Those options have been added to the config file as well. I also added some important classes to the global exclusion list to prevent things like toast notifications, action center, system tray overflow, etc. from having settings applied to those which would have unpleasant visual effects.

The thing that I am most excited for is even more performance improvements.

- Separated the hook Cases so that File Explorer has it's own Case

Separating the Case like that took some load off the main Case. The main case for all apps only need $EVENT_OBJECT_CREATE so the main. The File Explorer Case is for $EVENT_SYSTEM_FOREGROUND, $EVENT_OBJECT_FOCUS, $EVENT_OBJECT_REORDER.

I realized that File Explorer only loses the client area coloring when DwmExtendFrameIntoClientArea is used. Therefore, I made that Case discard everything right away unless DwmExtendFrameIntoClientArea is enabled. That way users who are not using that feature don't take any extra performance hit.

- Identifying File Explorer by class name now instead of process name

I used WinGetProcess and _ProcessGetName before to identify explorer.exe but it didn't perform as well. So I switched to _WinAPI_GetClassName only and identify File Explorer by class name "CabinetWClass" or "ExplorerWClass".

So that is two different small changes in 0.4 that had pretty decent performance improvements to the speed of the hook when under load and also the overall efficiency of the hook.

Posted

After releasing version 0.4 about an hour ago, I was thinking that there were likely no more performance gains to be found. I can't believe how wrong I was.

I was suspicious of _ProcessGetName being the cause of locking up my script for about a week now, especially during heavier load. That is exactly what drove me to find all of the previous performance savings. All of my previous performance savings was all for the purpose of trying to hit up _ProcessGetName as little as possible.

I decided to dig into the UDF functions and actually read the code for 4 or 5 different functions I was interested in. Reading the UDF function code is so eye-opening.

When I realized that _ProcessGetName was hitting up ProcessList() every single time, that set off alarm bells. I knew right away that it was the source of the occasional lockup and likely much higher CPU usage than it really needed to be. Especially since that was getting called dozens of times per second at times.

So I switched from _ProcessGetName to _WinAPI_GetWindowFileName because I already had a handle for each window from the hook function.

The difference is literally night and day with regard to CPU usage! 😃

And we still get the benefit of all of the previous performance improvements. The hook is even faster and for significantly less resources.

I will have to put together version 0.5 tomorrow.

Posted (edited)

I have updated the first post with version 0.5.

Switching from _ProcessGetName to _WinAPI_GetWindowFileName has resulted in the most dramatic performance improvement thus far. CPU usage is next to nothing now compared to before.

The benefit of _WinAPI_GetWindowFileName is the we get the full path now. This opens the door to using full path or just process name in the exclusions or inclusions. For example:

C:\Program Files\WindowsApps\Microsoft.WindowsNotepad_11.2503.16.0_x64__8wekyb3d8bbwe\Notepad\Notepad.exe
C:\Windows\notepad.exe
notepad.exe

We could do any variation of the above. We don't want to do client area coloring on the modern Notepad because it does not work properly. So it could be excluded, while allowing client area coloring on the classic win32 Notepad.

Also, since I'm using StringInStr now for comparing inclusion and exclusion lists, we could technically do portions of the path to keep it smaller. This should work already. But on this same thought, I should make it so that we can use wildcards, eg. C:\Program Files\WindowsApps\* or *\Microsoft.WindowsNotepad_11.2503.16.0_x64__8wekyb3d8bbwe\* but I will need to add some code to remove the asterisks.

Edited by WildByDesign
Posted

I have since added wildcards for process name and class name inclusions/exclusions.

Before I release another update, there is a bug that needs fixing and I need some help if possible, please.

Issue: Starting certain programs, particularly ones with a large number of controls, can sometimes cause the script to freeze. It won’t unfreeze until I close the app which triggered it. Then everything continues properly.

The good thing is that I have a way on my setup to trigger this issue approx. 50% of the time. So I can easily test various things to fix it.

Problem: I finally figured out the real cause of the issue. It is the WinGetTitle function that is causing it. It just gets stuck either on some specific calls or possibly just when it gets hammered too often. Then I assume that the script gets stuck waiting for something that never comes.

I would hate to get rid of it because skipping all blank windows is very useful and efficient.

Alternative: I have tried using _WinAPI_GetWindowText instead but it is very inconsistent. It does not lock up the script which is great. But sometimes it gives inconsistent results and WinGetTitle was always consistent.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...