Skip to content

Commit d884280

Browse files
committed
Fix transparency reassertion on shell events
1 parent 24d335f commit d884280

5 files changed

Lines changed: 121 additions & 29 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [1.0.1] - 2026-05-30
11+
12+
### Fixed
13+
14+
- Reassert the transparent accent and wallpaper-backing Z-order immediately
15+
when Explorer application buttons or the foreground window change, then
16+
once more after a short settling interval.
17+
- Keep the wallpaper-only taskbar background available when the Windows key
18+
opens shell UI from a foreground fullscreen application.
19+
20+
### Changed
21+
22+
- Distinguish foreground-location notifications from real foreground changes
23+
so ordinary window movements still update fullscreen state without
24+
repeatedly reapplying the taskbar style.
25+
1026
## [1.0.0] - 2026-05-30
1127

1228
### Added
@@ -22,5 +38,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2238
restoration scripts.
2339
- Regression tests, Windows CI, public documentation, and community files.
2440

25-
[Unreleased]: https://github.com/Mirochill/MiniTaskbar/compare/v1.0.0...HEAD
41+
[Unreleased]: https://github.com/Mirochill/MiniTaskbar/compare/v1.0.1...HEAD
42+
[1.0.1]: https://github.com/Mirochill/MiniTaskbar/compare/v1.0.0...v1.0.1
2643
[1.0.0]: https://github.com/Mirochill/MiniTaskbar/releases/tag/v1.0.0

‎README.md‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,17 @@ or moved and automatically rearms itself after an Explorer restart. When the
9393
button geometry changes, the group slides to its new center with a
9494
`180 ms` cubic ease-out transition.
9595

96-
MiniTaskbar also watches foreground-window and geometry changes. When the
97-
active window covers its complete monitor, the wallpaper backing is hidden
98-
immediately so games and videos receive an unobstructed fullscreen surface.
99-
It is restored as soon as fullscreen ends.
96+
Relevant Explorer and foreground-window changes also reassert the transparent
97+
accent and wallpaper-backing Z-order immediately, then once more after a short
98+
settling interval. This keeps the wallpaper-only background intact when an
99+
application opens without doing extra style work for ordinary window
100+
movements.
101+
102+
When the active window covers its complete monitor, the wallpaper backing is
103+
hidden immediately so games and videos receive an unobstructed fullscreen
104+
surface. Pressing the Windows key makes the backing available again while the
105+
shell UI is visible, and returning to the fullscreen application hides it
106+
again immediately.
100107

101108
## Build And Test
102109

‎docs/PERFORMANCE.md‎

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Performance
22

33
MiniTaskbar is designed to remain quiet while idle and to avoid desktop frame
4-
capture. This page documents the measurements collected for version `1.0.0`.
4+
capture. This page documents the measurements collected for version `1.0.1`.
55

66
## Design Principles
77

@@ -11,6 +11,9 @@ capture. This page documents the measurements collected for version `1.0.0`.
1111
application-button changes.
1212
- Accessibility state-only notifications are ignored because they do not
1313
change button geometry.
14+
- Application-button and real foreground changes reassert taskbar
15+
transparency immediately, then once more after a short settling interval.
16+
Foreground-location changes do not perform that extra style work.
1417
- Animation advances at approximately `60 fps` only while the application
1518
button group is moving.
1619
- Inexpensive fallback checks recover from Explorer changes without a busy
@@ -31,10 +34,10 @@ machine.
3134

3235
```text
3336
IdleSampleSeconds : 60
34-
CpuPercentOfOneCore : 0.547
35-
WorkingSetMiB : 24.70
36-
PrivateMemoryMiB : 22.53
37-
Handles : 218
37+
CpuPercentOfOneCore : 0.417
38+
WorkingSetMiB : 25.18
39+
PrivateMemoryMiB : 23.69
40+
Handles : 219
3841
Threads : 5
3942
BuiltExecutableBytes : 24064
4043
```
@@ -47,19 +50,22 @@ Focused probes also exercised the user-visible paths:
4750
TaskbarOverlayRect : [0,1040,1920,1080]
4851
WallpaperSource : WorkerW
4952
CenteredApplicationGroupDeltaPx : 0
50-
FullscreenBackingHiddenMs : 24
51-
FullscreenSurfaceClearMs : 30
52-
FullscreenExitReshowMs : 0
53-
AccentRecoveryMs : 4703
53+
FullscreenWinKeyBackingShownMs : <=47
54+
FullscreenWinKeyWallpaperMs : <=408
55+
FullscreenReturnBackingHiddenMs : <=29
56+
AppOpenAccentRecoveryMs : <=40
57+
FallbackAccentCadenceMs : 5000
5458
IconOpenFirstMoveDeltaPx : 23
5559
PurpleFallbackObserved : false
5660
UnderlyingWindowVisible : false
5761
```
5862

59-
The fullscreen probe used a true foreground fullscreen window. The accent
60-
recovery probe deliberately forced a colored taskbar state and measured the
61-
resident's periodic recovery. The icon probe added and removed an application
62-
button and observed the centered slide.
63+
The fullscreen probe used a true foreground fullscreen window, deliberately
64+
forced a purple taskbar state, pressed the Windows key, and returned to the
65+
fullscreen application. A separate probe forced the same purple state before
66+
opening an application and observed event-driven recovery before its first
67+
usable `40 ms` sample. The icon probe added and removed an application button
68+
and observed the centered slide.
6369

6470
## Interpretation
6571

‎src/Program.cs‎

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,14 @@ internal static int Run(int waitMilliseconds)
158158
var lastShellPoll = Stopwatch.StartNew();
159159
var lastCenterFallback = Stopwatch.StartNew();
160160
var eventSettle = new Stopwatch();
161+
var transparencySettle = new Stopwatch();
161162

162163
using (var backdrops = new WallpaperBackdropManager())
163164
using (var events = new TaskbarEventWatcher(previous))
164165
{
165166
bool fullscreen = FullscreenState.IsForegroundFullscreen();
166167
backdrops.SetVisible(!fullscreen);
167-
if (!backdrops.Refresh(previous, true)
168+
if (!backdrops.Refresh(previous, true, true)
168169
|| !TaskbarCenter.CenterImmediately(previous))
169170
{
170171
return 3;
@@ -177,7 +178,11 @@ internal static int Run(int waitMilliseconds)
177178
{
178179
bool settling = eventSettle.IsRunning
179180
&& eventSettle.ElapsedMilliseconds < EventSettleMilliseconds;
180-
int loopWaitMilliseconds = TaskbarCenter.HasActiveAnimation || settling
181+
bool transparencySettling = transparencySettle.IsRunning
182+
&& transparencySettle.ElapsedMilliseconds < EventSettleMilliseconds;
183+
int loopWaitMilliseconds = TaskbarCenter.HasActiveAnimation
184+
|| settling
185+
|| transparencySettling
181186
? AnimationFrameMilliseconds
182187
: PollMilliseconds;
183188
TaskbarEventKind eventKind = events.Wait(loopWaitMilliseconds);
@@ -187,13 +192,31 @@ internal static int Run(int waitMilliseconds)
187192
eventSettle.Restart();
188193
}
189194

195+
bool transparencyEvent = IsTransparencyReassertEvent(eventKind);
196+
if (transparencyEvent)
197+
{
198+
transparencySettle.Restart();
199+
}
200+
190201
bool currentFullscreen = FullscreenState.IsForegroundFullscreen();
191-
if (fullscreen != currentFullscreen)
202+
bool fullscreenChanged = fullscreen != currentFullscreen;
203+
if (fullscreenChanged)
192204
{
193205
fullscreen = currentFullscreen;
194206
backdrops.SetVisible(!fullscreen);
207+
}
208+
209+
bool transparencySettleDue = transparencySettle.IsRunning
210+
&& transparencySettle.ElapsedMilliseconds >= EventSettleMilliseconds;
211+
if (fullscreenChanged || transparencyEvent || transparencySettleDue)
212+
{
195213
TaskbarAccent.Apply(current, AccentState.EnableTransparentGradient);
214+
backdrops.Refresh(current, false, true);
196215
lastApplied.Restart();
216+
if (transparencySettleDue)
217+
{
218+
transparencySettle.Reset();
219+
}
197220
}
198221

199222
bool taskbarsChanged = false;
@@ -217,7 +240,10 @@ internal static int Run(int waitMilliseconds)
217240

218241
bool refreshSource =
219242
lastSourceRefresh.ElapsedMilliseconds >= SourceRefreshMilliseconds;
220-
backdrops.Refresh(current, refreshSource);
243+
backdrops.Refresh(
244+
current,
245+
refreshSource,
246+
taskbarsChanged || refreshSource);
221247
if (refreshSource)
222248
{
223249
lastSourceRefresh.Restart();
@@ -277,19 +303,27 @@ internal static bool HaveSameHandles(IList<IntPtr> left, IList<IntPtr> right)
277303

278304
return true;
279305
}
306+
307+
internal static bool IsTransparencyReassertEvent(TaskbarEventKind eventKind)
308+
{
309+
return eventKind == TaskbarEventKind.Centering
310+
|| eventKind == TaskbarEventKind.Foreground;
311+
}
280312
}
281313

282314
internal enum TaskbarEventKind
283315
{
284316
None,
285317
Centering,
286-
Foreground
318+
Foreground,
319+
ForegroundLocation
287320
}
288321

289322
internal sealed class TaskbarEventWatcher : IDisposable
290323
{
291324
private readonly AutoResetEvent _centeringChanged = new AutoResetEvent(false);
292325
private readonly AutoResetEvent _foregroundChanged = new AutoResetEvent(false);
326+
private readonly AutoResetEvent _foregroundLocationChanged = new AutoResetEvent(false);
293327
private readonly WaitHandle[] _waitHandles;
294328
private readonly ManualResetEvent _ready = new ManualResetEvent(false);
295329
private readonly NativeMethods.WinEventProc _callback;
@@ -306,7 +340,12 @@ internal sealed class TaskbarEventWatcher : IDisposable
306340
internal TaskbarEventWatcher(IList<IntPtr> taskbars)
307341
{
308342
_callback = OnWinEvent;
309-
_waitHandles = new WaitHandle[] { _centeringChanged, _foregroundChanged };
343+
_waitHandles = new WaitHandle[]
344+
{
345+
_centeringChanged,
346+
_foregroundChanged,
347+
_foregroundLocationChanged
348+
};
310349
Refresh(taskbars);
311350
_thread = new Thread(RunMessageLoop)
312351
{
@@ -325,8 +364,13 @@ internal TaskbarEventKind Wait(int milliseconds)
325364
return TaskbarEventKind.Centering;
326365
}
327366

328-
return result == 1
329-
? TaskbarEventKind.Foreground
367+
if (result == 1)
368+
{
369+
return TaskbarEventKind.Foreground;
370+
}
371+
372+
return result == 2
373+
? TaskbarEventKind.ForegroundLocation
330374
: TaskbarEventKind.None;
331375
}
332376

@@ -375,6 +419,7 @@ public void Dispose()
375419
_thread.Join(5000);
376420
_centeringChanged.Dispose();
377421
_foregroundChanged.Dispose();
422+
_foregroundLocationChanged.Dispose();
378423
_ready.Dispose();
379424
}
380425

@@ -481,7 +526,7 @@ private void OnWinEvent(
481526
if (eventType == NativeMethods.EventObjectLocationChange
482527
&& window == _foregroundWindow)
483528
{
484-
_foregroundChanged.Set();
529+
_foregroundLocationChanged.Set();
485530
}
486531

487532
if (IsCenteringEvent(eventType) && _windows.Contains(window))
@@ -576,7 +621,7 @@ internal sealed class WallpaperBackdropManager : IDisposable
576621
private IntPtr _source;
577622
private bool _visible = true;
578623

579-
internal bool Refresh(IList<IntPtr> taskbars, bool refreshSource)
624+
internal bool Refresh(IList<IntPtr> taskbars, bool refreshSource, bool reassert)
580625
{
581626
var activeTaskbars = new HashSet<IntPtr>(taskbars);
582627
var removed = new List<IntPtr>();
@@ -615,7 +660,7 @@ internal bool Refresh(IList<IntPtr> taskbars, bool refreshSource)
615660
_backdrops.Add(taskbar, backdrop);
616661
}
617662

618-
success = backdrop.Refresh(_source, refreshSource, _visible) && success;
663+
success = backdrop.Refresh(_source, reassert, _visible) && success;
619664
}
620665

621666
return success;

‎tests/MiniTaskbar.Tests.cs‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ private static int Main(string[] args)
3131
Run("Transparent accent avoids zero-color fallback", TestTransparentAccentColor);
3232
Run("Slide interpolation", TestSlideInterpolation);
3333
Run("WinEvent centering filter", TestWinEventCenteringFilter);
34+
Run("Transparency reassert event filter", TestTransparencyReassertEventFilter);
3435
Run("Fullscreen monitor coverage", TestFullscreenMonitorCoverage);
3536
Run("Transparent accent recovery cadence", TestTransparentAccentRecoveryCadence);
3637

@@ -190,6 +191,22 @@ private static void TestFullscreenMonitorCoverage()
190191
"A regular maximized window should not be treated as fullscreen.");
191192
}
192193

194+
private static void TestTransparencyReassertEventFilter()
195+
{
196+
AssertTrue(
197+
ResidentTaskbar.IsTransparencyReassertEvent(TaskbarEventKind.Centering),
198+
"Application-button changes should reassert transparency.");
199+
AssertTrue(
200+
ResidentTaskbar.IsTransparencyReassertEvent(TaskbarEventKind.Foreground),
201+
"Foreground changes should reassert transparency.");
202+
AssertFalse(
203+
ResidentTaskbar.IsTransparencyReassertEvent(TaskbarEventKind.ForegroundLocation),
204+
"Window movement should not repeatedly reassert transparency.");
205+
AssertFalse(
206+
ResidentTaskbar.IsTransparencyReassertEvent(TaskbarEventKind.None),
207+
"A timeout should not reassert transparency.");
208+
}
209+
193210
private static void TestTransparentAccentRecoveryCadence()
194211
{
195212
AssertEqual(5000, ResidentTaskbar.PeriodicReapplyMilliseconds);

0 commit comments

Comments
 (0)