melak47 / BorderlessWindow

basic win32 example of a borderless window (with aero shadows)
Creative Commons Zero v1.0 Universal
480 stars 76 forks source link

Is there any way to remove the 1px border? #13

Closed scp-r closed 6 years ago

scp-r commented 7 years ago

My recent project requires a semi-transparent borderless window. I'm using your method and direct2d for rendering on window 10: image (set alpha = 0.5F) image

(set alpha = 0.0F)

As you can see there is still a 1px border. Is there any way to remove it?

melak47 commented 7 years ago

Unfortunately I have come to the conclusion that it's not possible to paint over1 remove the one pixel border on Windows 10, see issue #11 if you care for some more details.

However, if you don't require the soft shadow, you can just use the basic_borderless window style (WS_POPUP derived) instead of aero_borderless, which does not come with such a border. It should still work with the glass/"blur behind" effect:

basic_borderless

  1. That's not quite accurate - you can paint over it (after moving it into the client area using DwmExtendFrameIntoClientArea and expanding the client area to the whole window in WM_NCCCALCSIZE), but as you can see, painting over it with a transparent color will not hide it.
ZimM-LostPolygon commented 7 years ago

Yes! I've found a way to have both the shadow and get rid of 1px border on Window 10. Unfortunately, it relies on undocumented SetWindowCompositionAttribute function. Here's the reference: https://gist.github.com/riverar/fd6525579d6bbafc6e48 The AccentState has to be set to ACCENT_ENABLE_GRADIENT. This is a Window 10 only hack, so you also have to check the OS version in runtime. Also, the shadow is the same size as for the non-focused window, and it doesn't changes its size when focused/non-focused.

ZimM-LostPolygon commented 7 years ago

@melak47 However... I've just tested your sample in Windows 7 (modified wcx.hbrBackground to make the background black), and the 1 px border is there as well. And it is just transparent, which looks weird. Am I missing something? I believed the issue was only with Windows 10. default

melak47 commented 7 years ago

@ZimM-LostPolygon the 1 px border you see is the result of calling DwmExtendFrameIntoClientArea. It's there on both Windows 7 and Windows 10, but for some reason Windows 10 paints over it using your background brush, while Windows 7 will not. You can paint over it yourself (e.g. using D2D, D3D, OGL, ...) -- but you have to use an opaque color -- @Jasper0819X wants to use some level of transparency for the blur behind effect to show, and that's why the border is showing through. Here's an example of going from fully opaque to fully transparent (with blur behind enabled): capture

So as long as you can put something opaque in your client area you should be ok.

ZimM-LostPolygon commented 7 years ago

@melak47 Hm... I am painting on a window using D3D, but the transparent border is still there on Windows 7 as well. Do I have to draw with some special coordinates or something?

melak47 commented 7 years ago

No special coordinates should be needed. Do you clear the backbuffer with an opaque color (say RGBA (0,0,0,1))?

melak47 commented 7 years ago

@ZimM-LostPolygon I just tried filling the window using D2D on Windows 7, and it works fine for me: d2d The code is here: https://github.com/melak47/BorderlessWindow/tree/d2d-fill if you want to compare. No need to use SetWindowCompositionAttribute.

ZimM-LostPolygon commented 7 years ago

@melak47 Ah, indeed, sorry. I was filling the window with RGBA(0,0,0,0). Making the color fully opaque fixed it on Windows 7.

My Windows 10 solution that uses the SetWindowCompositionAttribute function avoids this problem completely, since DwmExtendFrameIntoClientArea is not called at all. The only downside is that shadow is always the same shadow that is used for non-active windows. But for me this is better than the 1px border. Also, there shouldn't be any problems with controlling the opacity. Here's how it looks: default

ZimM-LostPolygon commented 7 years ago

@melak47 SetWindowCompositionAttribute trick is only for Windows 10. The AccentPolicy and related APIs are only available there. The 1px border on Windows 10 is the accent border...

melak47 commented 7 years ago

@ZimM-LostPolygon I can't quite reproduce your results. If I use ACCENT_ENABLE_GRADIENT without DwmExtendFrameIntoClientArea as you suggest, I get no aero shadow at all. (Also, now I can't use ACCENT_ENABLE_BLURBEHIND anymore, so what opacity is there to control?)

Aren't you going to draw to the window, anyway? Why is clearing with an opaque color a problem? :)

ZimM-LostPolygon commented 7 years ago

Well, it is a problem, since in the end, I want to actually use the alpha channel for transparent effects :)

Here's a messy C# code snippet that I used for the screenshot:

WinApi.DwmApi.Undocumented.AccentPolicy accent = new WinApi.DwmApi.Undocumented.AccentPolicy();
accent.AccentState = WinApi.DwmApi.Undocumented.AccentState.ACCENT_ENABLE_BLURBEHIND;
accent.AccentState = WinApi.DwmApi.Undocumented.AccentState.ACCENT_ENABLE_GRADIENT;
//accent.AccentState = WinApi.DwmApi.Undocumented.AccentState.ACCENT_ENABLE_TRANSPARENTGRADIENT;
accent.AccentFlags = (WinApi.DwmApi.Undocumented.AccentFlags) (0x20 | 0x40 | 0x80 | 0x100);
// Uncomment next line to disable shadows
//accent.AccentFlags = 0;
accent.GradientColor = 0;

int accentStructSize = Marshal.SizeOf(accent);
IntPtr accentPtr = Marshal.AllocHGlobal(accentStructSize);
Marshal.StructureToPtr(accent, accentPtr, false);

WinApi.DwmApi.Undocumented.WindowCompositionAttributeData data = new WinApi.DwmApi.Undocumented.WindowCompositionAttributeData();
data.Attribute = WinApi.DwmApi.Undocumented.WindowCompositionAttribute.WCA_ACCENT_POLICY;
data.SizeOfData = accentStructSize;
data.Data = accentPtr;

WinApi.DwmApi.Undocumented.SetWindowCompositionAttribute(hWnd, ref data);
Marshal.FreeHGlobal(accentPtr);

As for the opaque color... Well, initially, I wanted a window with both blur-behind and shadows. Using SetWindowCompositionAttribute with ACCENT_ENABLE_BLURBEHIND, I can do that: default The gray 1px border you can notice here is actually part of the shadow, it is half-transparent, so it looks fine on all backgrounds. And the opacity control using SetLayeredWindowAttributes works correctly, even without shadows: default So this is exactly what @Jasper0819X wanted.

If I don't want the blur-behind, but still want to have working transparency without the 1px accent color border, I can use ACCENT_ENABLE_TRANSPARENTGRADIENT with AccentPolicy.GradientColor set to 0x01000000 (0.39% of black, basically unnoticeable to a naked eye) and get this: default So basically... Using SetWindowCompositionAttribute removes the need to paint over with an opaque color, opening some new possibilities on Windows 10.

melak47 commented 7 years ago

Ah, those are the flags used to draw the borders on the start menu and the like.

enum AccentFlag {
    DrawLeftBorder   = 1 << 5,
    DrawTopBorder    = 1 << 6,
    DrawRightBorder  = 1 << 7,
    DrawBottomBorder = 1 << 8,
    DrawAllBorders   = (DrawLeftBorder | DrawTopBorder | DrawRightBorder | DrawBottomBorder),
};

I've seen someone write about these before, but I hadn't found a good use for them, thanks.

scp-r commented 7 years ago

I rebuilt my project on Windows Build 1703 with codes below:

static const MARGINS shadow_state{ 0,0,0,0 };
::DwmExtendFrameIntoClientArea(handle_, &shadow_state);

the border just disappeared and works fine with direct2d: image