protonscr

TBH: Task Bar Hero

protonopen appid 3678970Game compatibility - Unofficial
ValveSoftware/Proton#9841 · opened 2026-05-31 by RafaelHGOliveira · updated 2026-08-24 · 33 comments · github · game page · search this game
RRafaelHGOliveira 2026-05-31 github

Compatibility Report

  • Name of the game with compatibility issues: TBH: Task Bar Hero
  • Steam AppID of the game: 3678970

System Information

I confirm:

  • [x] that I haven't found an existing compatibility report for this game.
  • [x] that I have checked whether there are updates for my system available.

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

  1. Install TBH: Task Bar Hero (AppID 3678970)
  2. Set the compatibility tool to Proton Experimental (or Proton-CachyOS)
  3. Launch the game — the image appears, but all areas that should be transparent
    are rendered solid black
  4. Input does not reach the game window
  5. Switch to GE-Proton — input now works and the game is playable, but the
    transparent areas are still rendered solid black

Note

  • Only GE-Proton allows interacting with the game; Proton Experimental,
    Experimental bleeding-edge and Proton-CachyOS show the window but do not
    accept input.
  • The broken transparency (solid black instead of transparent) reproduces on
    every Proton version, GE-Proton included.

PROTON_LOG attached below (steam-3678970.log.gz).

steam-3678970.log.gz

Oooemir23 2026-06-03 github

Image that's the problem

Ssolarisfire 2026-06-03 github

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.

Tthekk1 2026-06-04 github

Additional diagnostic info

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:

Transparency: it's the DWM composition path, not WS_EX_LAYERED

The 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.

Input: Pointer Input API is stubbed

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".

Other observations (likely not the cause, just for completeness)

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.

Setup details

  • Distro: Linux Mint 22.x (Ubuntu noble base), kernel 6.17.0-29-generic, Cinnamon on X11
  • GPU: AMD Radeon 780M (Phoenix1 iGPU, RDNA3) — different vendor from OP's NVIDIA RTX 4070 SUPER, same symptom
  • Proton: GE-Proton10-34, wine-staging 10.0
  • Launch options used: PROTON_LOG=1 PROTON_USE_WINED3D=1 PROTON_NO_DESKTOP=1 %command%
  • WineD3D vs DXVK made no difference for the transparency symptom
  • Log channels were the default (no +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 visual

Happy to capture a more verbose log if helpful.

Ssolarisfire 2026-06-04 github
Image

Making progress...

Ssolarisfire 2026-06-04 github

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.

Ssolarisfire 2026-06-04 github

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 😅

Oooemir23 2026-06-05 github

How can I adjust this?

Tthekk1 2026-06-05 github

Patch is here: GloriousEggroll/[email protected]:proton-ge-custom:master but it's still not 100% perfect...
20260604_232811.webm

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 😅

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?

Tthaylorz 2026-06-09 github

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

  1. 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.

  2. 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.

  3. 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.

  4. 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 Vulkan compositeAlpha in 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.

shape_test.c — standalone unit test (gcc -O2 -o shape_test shape_test.c && ./shape_test)
/* 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;
}

Hhyoretsu 2026-06-09 github

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.

Image Image Image
Hhyoretsu 2026-06-09 github

Clicking through UI/popup rendering is also kinda wacky, I can't select stages all the time

Image

Also I just compared it to Proton-GE-10.34, the patch made my desktop randomly freeze for a few seconds a lot more.

Ssolarisfire 2026-06-09 github

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.

Tthaylorz 2026-06-09 github

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:

  1. DXVK's presenter created the swapchain with compositeAlpha = OPAQUE (hardcoded).
  2. The Vulkan client X window used the default 24-bit visual (no alpha).
  3. The top-level X window (the one the compositor actually composites) was 24-bit/opaque.

Fix (opt-in via WINE_LAYERED_OVERLAY_ALPHA=1, no effect on other games):

  • winex11: give the Vulkan client window and the top-level window a 32-bit ARGB visual
    with use_alpha, and stop Wine resetting the visual to opaque when the app toggles
    WS_EX_LAYERED.
  • DXVK presenter: request a non-opaque compositeAlpha — on X11 the surface exposes
    INHERIT (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.

Hhyoretsu 2026-06-10 github

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

Tthaylorz 2026-06-10 github

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.

Lluathebee 2026-06-10 github

@thaylorz , I just validated on the steamdeck and recomended your fix on protondb. awesome work mate!

Iils94 2026-06-11 github

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

Image
VV1nic1us 2026-06-12 github

Replying to https://github.com/ValveSoftware/Proton/issues/9841#issuecomment-4663109482

Image

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.

Wwillgonzaga 2026-06-12 github

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.

RRafaelHGOliveira 2026-06-13 github

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.

Rrafa4aquino 2026-06-14 github

Replying to [#9841 (comment)](https://github.com/ValveSoftware/Proton/issues/9841#issuecomment-4663109482)

Image 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.

Hhellmasterlinux 2026-06-14 github

Gracias!!!!!! se ve increible en cachyos!!!!

Image
Iissaren 2026-06-25 github

Image how to remove the blurred border?

DDo-tasarr 2026-06-25 github

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:

  • The overlay initially launches as a solid black box.
  • Input/clicks pass through momentarily during the splash/terms screen, but as soon as the main overlay loads, the background becomes completely pitch black and unclickable ("invisible" window blocking desktop interactions).
  • Launching with 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.

KKaldog06 2026-07-04 github

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:

  • Only XWayland windows are interactable through the game window.
  • If the game's current workspace has no other windows, or the window(s) behind it are NOT XWayland, all mouse/keyboard inputs will be instead redirected to that location on the last focused XWayland window's workspace.
  • When changing to a different workspace, the user must refocus the game window in order to send inputs to any XWayland window(s) behind it instead of the ones previously focused.

(XWayland Discord server icon being interacted with from a blank workspace and from a Wayland Firefox window)
Image Image

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)
Image
Image

Nnicolasmartin89 2026-07-11 github

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):

  • Editing ScreenManager registry values in the Proton prefix → game overwrites them on launch/exit
  • wmctrl -e to resize → the WM respects the game's WM_NORMAL_HINTS constraints
  • Unity CLI args (-screen-width, -screen-height, -screen-fullscreen 0) → ignored by Proton
  • Moving the Cinnamon panel to the top → game content still renders at the screen bottom

If 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

Tthaylorz 2026-07-20 github

@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 🙏

Tthaylorz 2026-07-20 github

@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.

Tthaylorz 2026-07-20 github

@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:

  1. MIT-SHM readback (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).
  2. Throttling the sampling in ALPHA mode: the input shape barely changes frame to frame, so there's no need to read back at full present rate.

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.

Tthaylorz 2026-07-20 github

@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.

SShambhalla 2026-07-25 github

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:

  • OS: Debian Trixie (Testing/13)
  • GPU: Nvidia RTX 3080 Ti
  • Driver Version: 610.43.02
  • Desktop Environment: KDE Plasma (Wayland)
  • Steam Platform: Flatpak
  • Proton Version: Proton Experimental / Proton 9.0

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.

Ddeus-est-machina 2026-08-10 · hidden on GitHub github

Codex Luna High was able to fix the black transparency issue on my system.

System:

  • Ubuntu 24.04.4 LTS
  • GNOME on X11
  • NVIDIA GeForce RTX 5090
  • NVIDIA driver 580.173.02
  • Native Steam installation
  • TBH: Task Bar Hero, AppID 3678970

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%
Ppinduzera 2026-08-24 github

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.