that's the problem
TBH: Task Bar Hero creates overlay windows with:
WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_NOACTIVATE
Under GE-Proton10-34, input works, but transparent overlay areas render as solid black.
The Proton log shows Wine creating "Overlay Box" windows with those layered/transparent styles, but x11drv reports shaped 0 for the corresponding X11 windows. GE-Proton also sets GAMESCOPE_XALIA_OVERLAY on them.
This suggests the Windows layered/transparent overlay is recognised but not being translated into an actual shaped/alpha X11 window.
Reproduced this on a different setup (Linux Mint Cinnamon / X11, NVIDIA, GE-Proton10-34, wine-staging 10.0) and pulled a PROTON_LOG=1 with the default channels. Two findings that may help narrow down where the actual fix needs to land:
WS_EX_LAYEREDThe game does not appear to use the classic WS_EX_LAYERED + UpdateLayeredWindow / SetLayeredWindowAttributes mechanism. It uses the modern DWM composition extension path. Relevant log lines:
fixme:dwmapi:DwmSetWindowAttribute (00000000000200B0, 2, 000000000010EF18, 4) stub
fixme:dwmapi:DwmExtendFrameIntoClientArea (00000000000200B0, 000000000010E620) stub
Both are pure stubs in current Wine's dwmapi — they return success but do nothing. Since DwmExtendFrameIntoClientArea (with negative MARGINS) is what tells the DWM to composite the host desktop through the client area, and it's a no-op, the supposedly-transparent regions end up rendered as the swapchain's clear color (black) on every Proton flavor.
This is consistent with the original report stating black-instead-of-transparent rendering reproduces on every Proton version including GE-Proton — the underlying dwmapi implementation is shared across all of them.
The game enables the Windows 8+ Pointer Input API:
fixme:win:NtUserEnableMouseInPointer enable 1 semi-stub!
fixme:win:GetPointerInfo (1 ...): stub
fixme:win:GetPointerType (1 ...): stub [354 calls in init phase]
EnableMouseInPointer is a semi-stub (returns success without actually switching the input model), and GetPointerInfo / GetPointerType are full stubs. This plausibly explains why input doesn't reach the game window on Proton Experimental / bleeding-edge / CachyOS, while GE-Proton happens to work — likely because of unrelated input-routing patches in GE that let classic WM_LBUTTONDOWN etc. through despite the pointer mode having been "enabled".
The game also calls UI Automation / MSAA against [#32769](/issue/ValveSoftware/Proton/32769) (the desktop window class) and walks the shell window hierarchy — presumably to position its sprites relative to real taskbar icons. Those calls are also stubbed (fixme:oleacc:find_class_data, fixme:uiautomation:...). GE-Proton spins up xalia.exe during launch but, as far as I understand, Xalia bridges Wine windows to host accessibility, not the other way around, so this functionality cannot work on Wine regardless.
PROTON_LOG=1 PROTON_USE_WINED3D=1 PROTON_NO_DESKTOP=1 %command%+win,+x11drv,+d3d), so window styles and X11 visuals aren't directly visible — a follow-up log with WINEDEBUG=+win,+x11drv,+dwmapi would confirm the DwmExtendFrameIntoClientArea margins value and whether the X11 window is created with an ARGB visualHappy to capture a more verbose log if helpful.
Making progress...
https://github.com/user-attachments/assets/e72c99d0-a5fe-404c-913a-25741ad756c2
Flickers, but it is possible to get this working on KDE plasma... just needs a little more tinkering wiithin proton.
Patch is here: https://github.com/GloriousEggroll/proton-ge-custom/compare/master...solarisfire:proton-ge-custom:master but it's still not 100% perfect...
https://github.com/user-attachments/assets/00c69747-2a09-4050-891a-26e2e8f7b9a1
I'm basically turning every black pixel transparent, but the devs don't make that easy with a load of black pixels in the game itself. I'm sure a proper proton dev can do a better job 😅
How can I adjust this?
Patch is here: GloriousEggroll/[email protected]:proton-ge-custom:master but it's still not 100% perfect...
20260604_232811.webmI'm basically turning every black pixel transparent, but the devs don't make that easy with a load of black pixels in the game itself. I'm sure a proper proton dev can do a better job 😅
I am not able to build it by my self.
Would it be possible for you to release a build and/or start the pull request to Proton GE?
Building on @Solarisfire's GE-Proton patch — thank you for the original work, it was the
right approach and got transparency rendering. I picked it up to tackle the remaining
flicker and make it shippable for other games, and wanted to share the results in case
it's useful for the PR.
What I changed on top of the original patch
Flicker fixed (temporal hysteresis). The flicker (worst when moving the window or
hovering the mouse) came from XGetImage reading the live X window mid-update: it
occasionally returns cleared/black frames, which shrank or collapsed the XShape region
for a frame and forced a reshape → flash. I made each 4×4 block stay in the shape for
N frames after it was last seen lit (*age = 12 instead of 2). Transient black
readbacks no longer shrink the shape; growth stays instant. On KDE Plasma Wayland the
result is transparent, responsive, and with no perceptible flicker, both when
moving the window and on hover.
Removed a latent freeze bug. An earlier attempt also kept the previous shape on any
all-black frame ("don't collapse to empty"). A unit test showed this is redundant
with the hysteresis for flicker AND freezes the shape forever if the overlay ever
goes genuinely empty (≥N black frames). Dropped it — the hysteresis alone absorbs
transients and still lets the window collapse correctly when truly empty.
Generalised (no hardcoded AppID). Instead of SteamGameId == "3678970", it's now
opt-in via WINE_LAYERED_OVERLAY_SHAPE=1 and triggers on any borderless
WS_EX_LAYERED window drawing through a 3D client surface. Off by default → zero effect
on other games. So it works for any DWM-glass-style overlay game, not just TBH.
Added a standalone unit test of the shape/hysteresis logic (compiles with gcc,
runs in milliseconds, no Proton build needed) — covers establish, transient black,
black bursts, content return, genuine shrink, persistent-black collapse, and first-frame.
Usage: add WINE_LAYERED_OVERLAY_SHAPE=1 %command% to the game's launch options.
WINEDEBUG=+x11drv shows layered overlay window ... shape updated with N rectangles.
Patch (against dlls/winex11.drv/{init,window}.c + x11drv.h) is up as a PR to
proton-ge-custom: GloriousEggroll/proton-ge-custom#553. The standalone unit test of the
shape/hysteresis logic is inlined below.
@Solarisfire — this builds entirely on your work and is not meant to compete with your
in-flight PR. If you'd rather land your own, just say so and I'll close #553 in favour of it;
or I'm glad to contribute these changes (hysteresis, generalisation, test) straight to your
branch. Your call — credit for the core approach is yours.
For anyone who just wants to try it now, there's an unofficial prebuilt (clearly marked
as not affiliated with GE-Proton/Valve): https://github.com/thaylorz/proton-ge-custom/releases/tag/proton-layered-overlay-v1
This remains an XShape (binary-mask) approach. The "fully correct" fix is real per-pixel
alpha (32-bit ARGB visual + non-opaque VulkancompositeAlphain the WSI/vkd3d path),
which would remove reshaping entirely — but that spans winex11 + Vulkan WSI + vkd3d-proton
and is a much larger change. The shaped approach is the practical, robust fix today.
/* Standalone unit test for the layered-overlay shaping logic in winex11.drv.
* Mirrors update_layered_overlay_shape(): XGetImage is replaced by a per-block
* visibility grid and XShapeCombineRectangles by a counter. Verifies that the
* temporal hysteresis prevents reshapes on transient black readbacks (the cause
* of flicker on window move / hover) while still collapsing when truly empty.
* build: gcc -O2 -o shape_test shape_test.c && ./shape_test
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef unsigned char BYTE;
typedef int BOOL;
#define TRUE 1
#define FALSE 0
#ifndef min
#define min(a,b) ((a)<(b)?(a):(b))
#endif
#define TBH_AGE 12 /* must match the constant in winex11.drv */
typedef struct { int x, y, width, height; } XRectangle;
typedef struct {
BYTE *shape_age;
unsigned shape_cols, shape_rows;
XRectangle *shape_rects;
unsigned shape_rect_count;
int changes_x, changes_y;
} Surface;
static int g_reshape_calls = 0;
static int g_combined_empty = 0;
static void reset_counters(void) { g_reshape_calls = 0; g_combined_empty = 0; }
static void XShapeCombine(int count) {
g_reshape_calls++;
if (count == 0) g_combined_empty++;
}
/* visible_blocks: cols*rows grid, 1 if the block has any non-black pixel. */
static BOOL update_shape(Surface *s, const BYTE *visible_blocks, int width, int height) {
unsigned cols = (width + 3) / 4, rows = (height + 3) / 4, x, y, count = 0, capacity;
BOOL actually_visible = FALSE;
XRectangle *rects;
if (cols != s->shape_cols || rows != s->shape_rows) {
free(s->shape_age);
s->shape_age = calloc(cols * rows, 1);
s->shape_cols = cols; s->shape_rows = rows;
free(s->shape_rects); s->shape_rects = NULL; s->shape_rect_count = 0;
}
if (!s->shape_age) return TRUE;
capacity = cols * rows;
rects = malloc(capacity * sizeof(*rects));
if (!rects) return TRUE;
for (y = 0; y < (unsigned)height; y += 4) {
int run_start = -1;
for (x = 0; x < (unsigned)width; x += 4) {
BYTE *age = &s->shape_age[(y / 4) * cols + x / 4];
BOOL visible = visible_blocks[(y / 4) * cols + x / 4];
/* hysteresis: keep a block in the shape for TBH_AGE frames after it
was last lit, so transient black readbacks don't shrink the shape */
if (visible) { *age = TBH_AGE; actually_visible = TRUE; }
else if (*age) --*age;
if (*age && run_start < 0) run_start = x;
if ((!*age || x + 4 >= (unsigned)width) && run_start >= 0) {
unsigned end = *age ? (unsigned)width : x;
rects[count].x = s->changes_x + run_start;
rects[count].y = s->changes_y + y;
rects[count].width = end - run_start;
rects[count].height = min(4, height - (int)y);
count++;
run_start = -1;
}
}
}
if (!s->shape_rects || count != s->shape_rect_count ||
(count > 0 && memcmp(rects, s->shape_rects, count * sizeof(*rects)))) {
XShapeCombine(count); /* count==0 only reachable on the first frame */
free(s->shape_rects);
s->shape_rects = rects;
s->shape_rect_count = count;
} else {
free(rects);
}
return actually_visible;
}
static int g_fail = 0;
#define CHECK(cond, msg) do { \
if (!(cond)) { printf(" [FAIL] %s\n", msg); g_fail++; } \
else { printf(" [ok] %s\n", msg); } } while (0)
int main(void) {
int W = 16, H = 16;
BYTE square[16] = {0,0,0,0, 0,1,1,0, 0,1,1,0, 0,0,0,0};
BYTE black[16] = {0};
BYTE shrunk[16] = {0,0,0,0, 0,1,0,0, 0,0,0,0, 0,0,0,0};
Surface s; memset(&s, 0, sizeof(s)); reset_counters();
printf("establish shape\n");
update_shape(&s, square, W, H);
unsigned base = s.shape_rect_count;
CHECK(base > 0 && g_reshape_calls == 1, "shape established in one reshape");
printf("transient black frame (move/hover)\n");
int before = g_reshape_calls;
update_shape(&s, black, W, H);
CHECK(g_reshape_calls == before, "no reshape on transient black (no flicker)");
CHECK(s.shape_rect_count == base, "shape preserved (not shrunk)");
CHECK(g_combined_empty == 0, "never collapsed to empty");
printf("burst of 5 black frames (< TBH_AGE)\n");
before = g_reshape_calls;
for (int i = 0; i < 5; i++) update_shape(&s, black, W, H);
CHECK(g_reshape_calls == before, "no reshape across the burst");
printf("content returns\n");
before = g_reshape_calls;
update_shape(&s, square, W, H);
CHECK(g_reshape_calls == before, "no reshape on return (shape already correct)");
printf("genuine shrink (black past TBH_AGE frames)\n");
update_shape(&s, square, W, H);
before = g_reshape_calls;
for (int i = 0; i < TBH_AGE + 2; i++) update_shape(&s, shrunk, W, H);
CHECK(g_reshape_calls > before, "genuine content change eventually reshapes");
CHECK(s.shape_rect_count > 0, "still non-empty after shrink");
printf("persistent black collapses correctly\n");
before = g_reshape_calls;
for (int i = 0; i < 30; i++) update_shape(&s, black, W, H);
CHECK(s.shape_rect_count == 0, "shape collapses when truly empty");
printf("first frame all black clips initial clear colour\n");
Surface s2; memset(&s2, 0, sizeof(s2));
int emp = g_combined_empty;
update_shape(&s2, black, W, H);
CHECK(g_combined_empty == emp + 1, "first all-black frame sets empty shape");
printf("\n%s\n", g_fail == 0 ? "ALL TESTS PASSED" : "FAILURES PRESENT");
return g_fail ? 1 : 0;
}
Way better than before, but it seems to still work mostly via black pixels (Synthesis slot background has transparent parts) and moving characters leave a black trail.
Clicking through UI/popup rendering is also kinda wacky, I can't select stages all the time
Also I just compared it to Proton-GE-10.34, the patch made my desktop randomly freeze for a few seconds a lot more.
Thanks for picking this up and running with it, @thaylorz — genuinely really happy to see it carried forward.
I’ve been pretty busy with work and my brother’s wedding, so I haven’t had the time to take my patch much further. It’s amazing to see someone else continue the work and improve it properly.
You’re absolutely not stepping on my toes at all. This looks like a much more complete and shippable version of the idea, so I’m very happy for #553 to move forward. Thanks again for building on it, and for being thoughtful about the credit.
Follow-up: got the fully-correct fix working — real per-pixel alpha, zero flicker (no
XShape). Sharing in case it helps land a proper solution.
Root cause (chain of opacity). The alpha has to survive D3D11 → DXVK → Vulkan swapchain →
X client window → X top-level window → compositor. It was being dropped at three points:
compositeAlpha = OPAQUE (hardcoded).Fix (opt-in via WINE_LAYERED_OVERLAY_ALPHA=1, no effect on other games):
use_alpha, and stop Wine resetting the visual to opaque when the app togglesWS_EX_LAYERED.compositeAlpha — on X11 the surface exposesINHERIT (and, with an ARGB window, PRE_MULTIPLIED), so we pick those instead of OPAQUE.The DXVK present shader already preserves the source alpha, so the game's real per-pixel
alpha then flows untouched to the compositor. Result on KDE Plasma Wayland: transparent,
interactive, zero flicker, with proper anti-aliased/semi-transparent edges (impossible
with the binary XShape mask).
The key insight was that the compositor composites the top-level window, not the Vulkan
child — fixing only the child still showed black until the top-level became ARGB too.
Caveat: the ALPHA path needs a compositing WM (KWin/Mutter/picom). The XShape path above
remains as a no-compositor fallback (WINE_LAYERED_OVERLAY_SHAPE=1).
Patches (winex11 + DXVK presenter, committed/viewable) are in a dedicated PR:
GloriousEggroll/proton-ge-custom#555. An unofficial prebuilt is at
https://github.com/thaylorz/proton-ge-custom/releases/tag/proton-layered-overlay-v1
(#553 is the simpler XShape-only fallback.)
Core XShape approach + the original investigation: @Solarisfire — this builds on your work.
It's perfect now! Btw, I think placing it above the taskbar is a limitation of Plasma KDE right?
Maybe just that it still renders an invisible big window that doesn't let click pass through it? Not sure how it works on Windows
Thanks — glad it's working for you too!
And yeah, I noticed the same click issue. In the alpha mode the window is visually transparent but still occupies the full rectangle for input, so clicks on the transparent areas don't pass through to whatever is behind it. I'm already working on a fix: setting a per-pixel input shape (XShape ShapeInput) from the rendered content, so input only lands on the actual (opaque) pixels and the transparent areas become click-through. The nice part is that input-shape changes don't trigger a repaint, so it adds click-through without bringing the flicker back.
On the "above the taskbar" point — I think that's a separate thing, and likely a Plasma/window-manager limitation rather than something this fix controls. On KDE the panel lives in a high stacking layer, and normal windows generally can't draw above it; on Windows the taskbar isn't an "always-on-top" layer in the same way, so a topmost window sits over it. It might be approachable with window-type/layer hints (dock type, _NET_WM_STATE_ABOVE, or wlr-layer-shell on Wayland), but that's a different problem from the transparency/input one and could just be a WM limitation. I'll keep it in mind, but it's out of scope for this patch.
@thaylorz , I just validated on the steamdeck and recomended your fix on protondb. awesome work mate!
Replying to https://github.com/ValveSoftware/Proton/issues/9841#issuecomment-4655515394
LETS GO!!!
Bazzite - Fedora, working really well with Always on Top in-game settings
Replying to https://github.com/ValveSoftware/Proton/issues/9841#issuecomment-4663109482
For some reason, I ended up with this gap next to the taskbar.
I have a dual-screen setup and noticed that it functions properly on the second display.
I'm having the same problem, but now that the transparency and click issues have been fixed, it's playable.
I think this problem is due to interference from KDE.
Side-effect with WINE_LAYERED_OVERLAY_SHAPE=1: mouse input lag in a simultaneously-running native game (CS2)
Using the XShape build (WINE_LAYERED_OVERLAY_SHAPE=1, the #553 path) with TBH running alongside native CS2: CS2 reports ~200 FPS but mouse movement feels like ~30 FPS — sluggish/stuttery input. Closing TBH immediately restores normal mouse response in CS2. Without the env var (plain GE-Proton, TBH still running), the issue does not occur.
Setup: CachyOS, KDE Plasma 6 Wayland, RTX 4070 SUPER. Note that on a Wayland session both apps share the same XWayland server (TBH via winex11 → XWayland, native CS2 via XWayland).
My guess: the periodic XGetImage readbacks used to compute the shape mask are stalling the shared XWayland server, adding latency to mouse events for other X clients on it — including the native game. This would be specific to the SHAPE path; the ALPHA path (#555) shouldn't have this cost since it doesn't read back pixels.
Replying to [#9841 (comment)](https://github.com/ValveSoftware/Proton/issues/9841#issuecomment-4663109482)
For some reason, I ended up with this gap next to the taskbar.
I have a dual-screen setup and noticed that it functions properly on the second display.
I got the same issue, dual monitor setup and i got the same gap.
edit: with kde too
edit2: if u put in 1.25 size u can drag to taskbar.
Gracias!!!!!! se ve increible en cachyos!!!!
how to remove the blurred border?
Hi everyone,
I'm testing the official upstream integration of the transparent overlay fixes (merged into GE-Proton 11-1) with "TBH: Task Bar Hero" on Bazzite (KDE Plasma 6.6.5 / Wayland).
While thaylorz's standalone build (Proton-LayeredOverlay) worked perfectly fine with the WINE_LAYERED_OVERLAY_ALPHA=1 flag, the official GE-Proton 11-1 release seems to break the alpha channel rendering.
Symptoms on GE-Proton 11-1:
WINE_LAYERED_OVERLAY_ALPHA=1 or WINE_LAYERED_OVERLAY_SHAPE=1 no longer strips the black background, and forcing PROTON_USE_WINED3D=1 did not fix the transparency either.It looks like the rebase onto the newer Proton 11 upstream/DXVK changes might have caused a conflict with how the alpha channel (32-bit ARGB) is being passed down to KWin on Wayland.
Using Arch/CachyOS + Hyprland with GE-Proton 11-1 and the WINE_LAYERED_OVERLAY_ALPHA=1 launch option allows for game transparency, however some windows behind the game remain non-interactable:
(XWayland Discord server icon being interacted with from a blank workspace and from a Wayland Firefox window)
The middle of the setting buttons are unclickable and treated as a click-through area while still being visible. (unsure if this is a Hyprland exclusive issue?)
(XWayland Discord channel list and Steam library being resized through settings)
Regarding the positioning issue mentioned by @V1nic1us and @willgonzaga: I ran the game with WINEDEBUG=+x11drv and found the exact cause.
The game creates a second overlay window that forces its size to 970×892 via WM_NORMAL_HINTS with min=max, making it impossible for the window manager to resize or reposition it:
0140:trace:x11drv:window_set_wm_normal_hints ...
WM_NORMAL_HINTS { pos 200,34 min 970,892 max 970,892 grav 10 }
0140:trace:x11drv:window_set_wm_normal_hints ...
WM_NORMAL_HINTS { pos 324,-194 min 970,892 max 970,892 grav 10 }
0140:trace:x11drv:window_set_wm_normal_hints ...
WM_NORMAL_HINTS { pos 324,25 min 970,892 max 970,892 grav 10 }
The game first requests position Y=-194 (above the screen), likely trying to align its visible area with the bottom edge. The window manager adjusts it to Y=25, but since the window is 892px tall on a 768px screen, the bottom ~124px are always off-screen.
As @thekk1 pointed out, the game uses UI Automation against the desktop to detect the taskbar position (fixme:oleacc:find_class_data, fixme:uiautomation stubs). Since these APIs are stubs in Wine, the game can't find the taskbar and falls back to hardcoded dimensions (970×892).
What I tried (all failed):
wmctrl -e to resize → the WM respects the game's WM_NORMAL_HINTS constraints-screen-width, -screen-height, -screen-fullscreen 0) → ignored by ProtonIf Wine/Proton could relax the WM_NORMAL_HINTS constraints from the application side, or if the game properly detected the workarea height, the window could be resized to fit the screen.
System: Linux Mint 22.3 Cinnamon/X11, AMD Vega 8, Mesa 25.2.8, GE-Proton11-1, 1366×768
@Do-tasarr thanks for the detailed report — this is genuinely useful.
The key clue is that on GE-Proton11-1 the ALPHA path works on Hyprland (see @Kaldog06's report) but shows black on KWin/Wayland, while the older standalone build works on that same KWin. Since the DXVK swapchain is identical in both cases, DXVK isn't the culprit — if it were, Hyprland would be black too.
What that points at is the top-level window losing its 32-bit ARGB visual on the newer Proton 11 base. A Wine window is really two nested X windows (a top-level "whole" window and the child window the Vulkan swapchain renders into). The compositor composites the top-level, so it must itself carry alpha — the child being ARGB isn't enough. KWin enforces that strictly (opaque top-level → black); Hyprland is more lenient and honors the child's alpha, which is why it still looks transparent there. My working theory is that on Proton 11 the top-level gets its ARGB visual set at creation but a code path resets it back to the default opaque visual afterwards.
Before I patch blind, could you grab two logs so I can confirm which layer regressed?
1. Is DXVK still requesting non-opaque compositeAlpha?
# launch options:
PROTON_LOG=1 WINE_LAYERED_OVERLAY_ALPHA=1 %command%
# then:
grep -i "Layered overlay: compositeAlpha" ~/steam-*.log
compositeAlpha 8 (INHERIT) is what we want on X11; 1 (OPAQUE) would mean DXVK didn't take.
2. Does the top-level window keep the ARGB visual + use_alpha?
# launch options:
WINEDEBUG=+x11drv WINE_LAYERED_OVERLAY_ALPHA=1 %command% 2> ~/tbh-x11.log
# then:
grep -Ei "layered overlay: using ARGB|create_whole_window|set_window_visual|use_alpha" ~/tbh-x11.log
If (1) shows 8 and (2) shows the top-level reverting to the default visual, it's a one-spot winex11 fix (keep ARGB + use_alpha across window recreate on the Proton 11 base). If you can also confirm your KWin/Plasma version and whether it reproduces on an X11 session (not just Wayland), that'll help narrow it further. Appreciate it 🙏
@nicolasmartin89 excellent RCA — thank you for going all the way to the WM_NORMAL_HINTS trace. That confirms it: the game uses UI Automation to locate the taskbar, those APIs (oleacc/uiautomation) are stubs in Wine, so it falls back to a hardcoded 970×892 window with min==max hints and an off-screen Y. On a screen shorter than 892 px it physically can't fit, which is why none of the reposition workarounds stick.
So the real fix lives above Proton's transparency layer — either UIA desktop-tree support in Wine, or the game falling back to SystemParametersInfo(SPI_GETWORKAREA) (which Wine does answer correctly from _NET_WORKAREA, minus the panel) instead of UIA. Both are bigger than the overlay patch.
There's one overlay-scoped thing I want to test: the game deliberately requests a negative Y (Y=-194) to align its visible bottom edge, and the WM clamps it on-screen, pushing the content off the bottom. For these borderless overlay windows it may be correct to honor the app's requested position instead of clamping it — that could make the game's intended alignment land. It's gated behind the overlay env var so it can't affect normal windows. I'll report back if it helps.
For now, @rafa4aquino's tip (game scale 1.25 + drag) is the practical workaround on short screens.
@RafaelHGOliveira your diagnosis is spot on. The shape path does a full-window XGetImage every present — for a ~970×892 window that's ~3.4 MB copied synchronously over the X socket per frame, and on a Wayland session that readback serializes on the shared XWayland server, adding latency to every other X client on it (your native CS2 included). Closing TBH removes the readback, which is why CS2 recovers instantly.
Two things I'm fixing:
XShmGetImage instead of XGetImage): the image lands in a shared-memory segment, so the multi-MB per-frame socket transfer disappears (there's still one round-trip, but it's far cheaper). Falls back to XGetImage when SHM isn't available (e.g. a remote display).One small correction to your note: the ALPHA path also reads back per frame today (to compute the click-through input shape), so it currently carries the same cost — both paths get the win from the above. I'll follow up with a patch to test.
@Kaldog06 thanks — there are actually two separate things in your report:
1. Only XWayland windows behind are interactable / input gets mis-routed. The game runs under XWayland, and the click-through is implemented as an X input shape, so it only governs input routing among X clients. Native Wayland surfaces underneath are invisible to XWayland's input routing, so a click on a transparent area either hits the last-focused XWayland window or nothing. This is an XWayland/compositor input-region limitation rather than something Proton can fix — an X11 desktop session avoids it entirely, and it's worth raising on the Hyprland/XWayland side. Same root cause behind the cross-workspace refocus behavior you described.
2. Visible buttons in the middle acting as click-through. This one is on us and is fixable. The input shape is currently derived from an RGB "non-black" test, so dark-but-opaque UI (like those setting buttons) gets misclassified as transparent and becomes click-through. In alpha mode the window is real 32-bit ARGB, so the fix is to key the input shape off the actual alpha channel instead of pixel brightness — then dark opaque content stays clickable. I have that change written and will post a patch to test.
Hello, reporting that the opaque black box issue for TBH: Task Bar Hero persists on the latest Nvidia 610 series drivers under a Wayland session.
System Specs:
When running the game on official Proton versions, the game launches stable and audio works, but the window renders as a solid opaque black rectangle over the taskbar instead of handling per-pixel alpha transparency.
I have tried forcing dxvk.enableAlphaToCoverage = True inside dxvk.conf and injecting PROTON_OLD_WINDOW_REPARENTING=1, but the Nvidia 610 driver under Wayland seems to strictly reject legated ARGB 32-bit transparency composition via XWayland, forcing a solid black background. The only workaround to get transparency right now is to fall back to an X11 session entirely.
Codex Luna High was able to fix the black transparency issue on my system.
System:
With Proton 9 Beta and Proton Experimental, the game window was created as a 24-bit opaque X11 window (Depth: 24), which caused all transparent regions to appear black.
Codex Luna High installed official GE-Proton11-3 and selected it specifically for AppID 3678970. Codex Luna High then added this launch option:
WINE_LAYERED_OVERLAY_ALPHA=1 %command%
Just as a reminder, the "On-together" (3707400) game also uses some transparency things. Not sure if its internals is like TBH, but would be cool if also worked there.
proton experimentalx4 2026-08ge-proton11-3x1 2026-08proton 9.0x1 2026-07ge-proton11-1x2 2026-07ge-proton10-34x2 2026-06WINE_LAYERED_OVERLAY_ALPHA=1x3 2026-08PROTON_OLD_WINDOW_REPARENTING=1`,x1 2026-07PROTON_LOG=1x2 2026-07WINEDEBUG=+x11drvx1 2026-07WINEDEBUG=+x11drv`x2 2026-07PROTON_USE_WINED3D=1`x1 2026-06WINE_LAYERED_OVERLAY_ALPHA=1`x1 2026-06WINE_LAYERED_OVERLAY_SHAPE=1`x1 2026-06WINE_LAYERED_OVERLAY_SHAPE=1`,x1 2026-06WINE_LAYERED_OVERLAY_SHAPE=1`:x1 2026-06WINE_LAYERED_OVERLAY_ALPHA=1`,x1 2026-06WINE_LAYERED_OVERLAY_SHAPE=1`).x1 2026-06WINE_LAYERED_OVERLAY_SHAPE=1x1 2026-06WINE_LAYERED_OVERLAY_SHAPE=1`**x1 2026-06PROTON_LOG=1`x1 2026-06
Compatibility Report
System Information
I confirm:
Symptoms
TBH: Task Bar Hero renders its UI as a transparent/layered window over the
desktop/taskbar. Under Proton, every region that should be transparent is drawn
as solid black on every Proton version tested. The game image does appear,
but mouse input only reaches the game under GE-Proton — with Proton
Experimental (and its bleeding-edge branch) and Proton-CachyOS the window shows
but is not interactive. The black-instead-of-transparent rendering happens on
all versions, GE-Proton included.
Reproduction
are rendered solid black
transparent areas are still rendered solid black
Note
Experimental bleeding-edge and Proton-CachyOS show the window but do not
accept input.
every Proton version, GE-Proton included.
PROTON_LOG attached below (
steam-3678970.log.gz).steam-3678970.log.gz