X11/GLX window above desktop - c++

I want create opengl application for desktop, but icons and wallpaper are break down.
Window should be under icons:
code for create window:
XSetWindowAttributes swa;
swa.background_pixmap = ParentRelative;
swa.background_pixel = 0;
swa.border_pixmap = 0;
swa.border_pixel = 0;
swa.bit_gravity = 0;
swa.win_gravity = 0;
swa.override_redirect = True;
swa.colormap = XCreateColormap(dis, root, vi->visual, AllocNone);
swa.event_mask = StructureNotifyMask | ExposureMask;
unsigned long mask = CWOverrideRedirect | CWBackingStore | CWBackPixel | CWBorderPixel | CWColormap;
window = XCreateWindow(display, desktop, 0, 0,
display_width, display_height, 0, vi->depth,
InputOutput, vi->visual, mask, &swa); // vi -XVisualInfo
XLowerWindow(display, window);
long value = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DESKTOP", false);
XChangeProperty(display, window,
XInternAtom(display, "_NET_WM_WINDOW_TYPE", false),
XA_ATOM, 32, PropModeReplace, (unsigned char *) &value, 1);
Atom xa;
xa = ATOM(_WIN_LAYER);
if (xa != None) {
long prop = 0;
XChangeProperty(display, window, xa, XA_CARDINAL, 32,
PropModeAppend, (unsigned char *)&prop, 1);
}
xa = ATOM(_NET_WM_STATE);
if (xa != None) {
Atom xa_prop = ATOM(_NET_WM_STATE_BELOW);
XChangeProperty(display, window, xa, XA_ATOM, 32, PropModeAppend,
(unsigned char *)&xa_prop, 1);
}
if (transparency < 1.0) {
uint32_t cardinal_alpha = (uint32_t) (transparency * (uint32_t)-1) ;
XChangeProperty(display, window,
XInternAtom(display, "_NET_WM_WINDOW_OPACITY", 0),
XA_CARDINAL, 32, PropModeReplace, (uint8_t*) &cardinal_alpha, 1);
}
XLowerWindow(display, window);
ctx = glXCreateContextAttribsARB(dis, fbc, NULL, True, gl3attr); //ctx = GLXContext
next i create XMapWindow(dis, window), glXMakeCurrent(dis, window, ctx), glViewport and clearColor(red)\swapBuffers (in cycle)
note: ATOM = #define ATOM(a) XInternAtom(dis, #a, False)

Unfortunately there is no solution that works in 100% of all the cases. The most immediate solution would be to draw directly to the root window: open connection to X, get xid of root window, query what visual it's configured to, created compatible GLX context, draw to that. (this works somewhat reliably for X11; doing similar on Microsoft Windows or macOS is impossible though.)
However you can't change the visual/pixelformat, and just drawing to the root window won't necessarily make it an "underlay" for icons on the desktop. You see, most "desktops" are implemented by creating their very own full screen window, on top of the root window (but beneath everything else), and will even be used to draw the wallpaper then.
Drawing directly to the root window would give you the desired effect only, if the desktop environment of choice would cooperate and draw its desktop and the icons on it as a transparent, composited window; or if it'd mask out the icons using the X shape extension (I'm not aware of any DE that does either).

Related

Partially transparent window OpenGL/Win32

I have read every existing question on this tricky (niche) subject, but I am stuck. I have a Win32 window with an OpenGL context. I want my window to be partially transparent.
My result so far is that the entirety of the window is transparent. I want only the black area to be transparent, so that I can draw some 3D objects and they will look like they are coming of the window.
First, in my window class, I have set hbrBackground to black.
Windows::WindowClass windowClass;
windowClass.cbSize = sizeof(Windows::WindowClass);
windowClass.style = Windows::CS_VREDRAW | Windows::CS_HREDRAW;
windowClass.lpfnWndProc = WndProc;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = moduleHandle;
windowClass.hIcon = Windows::LoadIcon(0u, (X*)Windows::IDI_QUESTION);
windowClass.hCursor = Windows::LoadCursor(0u, (X*)Windows::IDC_ARROW);
windowClass.hbrBackground = Windows::CreateSolidBrush(0x00000000);
windowClass.lpszMenuName = nullptr;
windowClass.lpszClassName = (X*)name;
windowClass.hIconSm = Windows::LoadIcon(0u, (X*)Windows::IDI_QUESTION);
I have created my window with the WS_EX_LAYERED flag.
windowHandle = Windows::CreateWindow(Windows::WS_EX_LAYERED, (X*)name, "", Windows::WS_POPUP, w / 4, h / 4, w / 2, h / 2, 0u, 0u, moduleHandle, 0u);
In my pixel format, I have enabled alpha and composition.
PixelFormatDescriptor format;
format.nSize = sizeof(PixelFormatDescriptor);
format.nVersion = 1;
format.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER | PFD_SUPPORT_COMPOSITION;
format.iPixelType = PFD_TYPE_RGBA;
format.cColorBits = 32;
format.cRedBits = 0;
format.cRedShift = 0;
format.cGreenBits = 0;
format.cGreenShift = 0;
format.cBlueBits = 0;
format.cBlueShift = 0;
format.cAlphaBits = 8;
format.cAlphaShift = 0;
format.cAccumBits = 0;
format.cAccumRedBits = 0;
format.cAccumGreenBits = 0;
format.cAccumBlueBits = 0;
format.cAccumAlphaBits = 0;
format.cDepthBits = 24;
format.cStencilBits = 8;
format.cAuxBuffers = 0;
format.iLayerType = PFD_MAIN_PLANE;
format.bReserved = 0;
format.dwLayerMask = 0;
format.dwVisibleMask = 0;
format.dwDamageMask = 0;
I have tried the blur region "trick", but it has no effect. My result is not related to this piece of code.
struct DwmBlurBehind
{
U4 dwFlags;
S4 fEnable;
X* blurRegionHandle;
S4 fTransitionOnMaximized;
};
DwmBlurBehind blurBehind;
blurBehind.dwFlags = Windows::DWM_BB_ENABLE | Windows::DWM_BB_BLURREGION;
blurBehind.fEnable = true;
blurBehind.blurRegionHandle = Windows::CreateRectRgn(0, 0, -1, -1);
blurBehind.fTransitionOnMaximized = false;
Windows::DwmEnableBlurBehindWindow(windowHandle, &blurBehind);
Finally, I have set the LWA_COLORKEY and LWA_ALPHA attributes. This is what gave me the effect displayed. However, the color key does not seem to be taken into account (I have tried non-zero values as well).
Windows::SetLayeredWindowAttributes(windowHandle, 0, 170, Windows::LWA_COLORKEY | Windows::LWA_ALPHA);
I did not forget to enable blending.
GL::Enable(GL::BLEND);
GL::BlendFunc(GL::SRC_ALPHA, GL::ONE_MINUS_SRC_ALPHA);
What you want to do requires window compositing which has been around since Windows Vista, so essentially every version of Windows you have to care about (Windows XP and earlier are at End of Life).
The key steps to take is, to enable DWM intra window compositing by enabling "Blur Behind Window" and use a WM_POPUP window; if you do not use WM_POPUP style the window manager will draw decorations and your OpenGL rendering will "hover" above that.
DWM_BLURBEHIND bb = {0};
bb.dwFlags = DWM_BB_ENABLE;
bb.fEnable = TRUE;
bb.hRgnBlur = NULL;
DwmEnableBlurBehindWindow(hWnd, &bb);
MARGINS margins = {-1};
impl_DwmExtendFrameIntoClientArea(hWnd, &margins);
Next, you must create an OpenGL context using the newer "attribute" API instead of using the pixelformat descriptor selection. With the attribute API you can select a transparent with alpha window format.
int attribs[] = {
WGL_DRAW_TO_WINDOW_ARB, TRUE,
WGL_DOUBLE_BUFFER_ARB, TRUE,
WGL_SUPPORT_OPENGL_ARB, TRUE,
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
WGL_TRANSPARENT_ARB, TRUE,
WGL_COLOR_BITS_ARB, 32,
WGL_RED_BITS_ARB, 8,
WGL_GREEN_BITS_ARB, 8,
WGL_BLUE_BITS_ARB, 8,
WGL_ALPHA_BITS_ARB, 8,
WGL_DEPTH_BITS_ARB, 24,
WGL_STENCIL_BITS_ARB, 8,
0, 0
};
INT iPF;
UINT num_formats_choosen;
if( !wglChoosePixelFormatARB(
hDC,
attribs,
NULL,
1,
&iPF,
&num_formats_choosen) ) {
fprintf(stderr, "error choosing proper pixel format\n");
return NULL;
}
if( !num_formats_choosen ) {
return NULL;
}
PIXELFORMATDESCRIPTOR pfd;
memset(&pfd, 0, sizeof(pfd));
/* now this is a kludge; we need to pass something in the PIXELFORMATDESCRIPTOR
* to SetPixelFormat; it will be ignored, mostly. OTOH we want to send something
* sane, we're nice people after all - it doesn't hurt if this fails. */
DescribePixelFormat(hDC, iPF, sizeof(pfd), &pfd);
if( !SetPixelFormat(hDC, iPF, &pfd) ) {
fprintf(stderr, "error setting proper pixel format\n");
ReleaseDC(hWnd, hDC);
DestroyWindow(hWnd);
return NULL;
}
I've got a complete working example for this in my wglarb wrapper repository over at GitHub:
https://github.com/datenwolf/wglarb/blob/master/test/layered.c
You might be missing the call to UpdateLayeredWindow . IIRC, not calling UpdateLayeredWindow results in some weird behavior. YMMV.
More details here
According with SetLayeredWindowAttributes doc you need to pass
a COLORREF structure that specifies the transparency color key to be
used when composing the layered window. All pixels painted by the
window in this color will be transparent. To generate a COLORREF, use
the RGB macro.
Review the second parameter of your call to this function.

DX9 switching from full screen to window give wrong client area

Having a bit of a problem in my dx9 application.
When I switch back from Full screen to windowed mode using the code below the client area is not the right size, it's smaller.
The AdjustWindowRect function is doing its task correctly but SetWindowPos does not set the right window size. Perhaps I'm missing something. Any ideas .
if (d3dpp.Windowed && resChoice == resolutionchoice) return false; // already in window mode and same resolution
//MessageBox(NULL, L"switching to window", L"ERROR", MB_OK);
resChoice = resolutionchoice;
screenWidth = resolutionwidth[resChoice];
screenHeight = resolutionheight[resChoice];
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; // set the back buffer format to 32-bit
d3dpp.BackBufferWidth = screenWidth; // set the width of the buffer
d3dpp.BackBufferHeight = screenHeight; // set the height of the buffer
d3dpp.Windowed = true;
SetWindowLong(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW); // WS_CAPTION | WS_SYSMENU | WS_VISIBLE | WS_MINIMIZEBOX );
// need to call SetWindowPos as well
string values;
RECT r = { 0,0,screenWidth,screenHeight };
AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, false);
values = std::to_string(r.left);
OutputDebugStringA("Adjust area = ");
OutputDebugStringA(values.c_str()); OutputDebugStringA(",");
values = std::to_string(r.top);
OutputDebugStringA(values.c_str()); OutputDebugStringA(",");
values = std::to_string(r.right);
OutputDebugStringA(values.c_str()); OutputDebugStringA(",");
values = std::to_string(r.bottom);
OutputDebugStringA(values.c_str()); OutputDebugStringA("\n");
SetWindowPos(hWnd, HWND_TOP, r.left, r.top, r.right - r.left , r.bottom - r.top, SWP_NOZORDER | SWP_SHOWWINDOW | SWP_FRAMECHANGED);//
//screenWidth = SCREEN_WIDTH;
//screenHeight = SCREEN_HEIGHT;
//windowXscale = 1;
//windowYscale = 1;
GetClientRect(hWnd, &r);
values = std::to_string(r.left);
OutputDebugStringA("Client area = ");
OutputDebugStringA(values.c_str()); OutputDebugStringA(",");
values = std::to_string(r.top);
OutputDebugStringA(values.c_str()); OutputDebugStringA(",");
values = std::to_string(r.right);
OutputDebugStringA(values.c_str()); OutputDebugStringA(",");
values = std::to_string(r.bottom);
OutputDebugStringA(values.c_str()); OutputDebugStringA("\n");
}
After some more searching discovered that if you use SetWindowPos and attempt to make a window that is bigger than the desktop or when returning from Full screen in this case, windows will send a message and the window size will be adjusted automatically to make it no bigger than the current desktop size.
Adding SWP_NOSENDCHANGING flag in the SetWindowPos api call stops this happening and the client size will be what you wanted.

Child window not showing up in x11

I have created two windows. One with 32 bit and the other with 24 bits. The 32 bit window renders opengl and creates graphics with an alpha component onto it. The 24 bit window renders video. Both of the windows are child of a main window which is 32 bits. To my knowledge, when I call XRestackWindows, it show stack the windows such that the 32 bit window is on the top while also showing the 24 bit window underneath. This is not the case. When I do so, it only shows the 32 bit window. I know the 24 bit window is there because if I switch the order, then I only get to see the 24 bit window. Why is this the case?
/* Open the standard display (the primary screen) */
x_display = XOpenDisplay ( NULL );
visual_template.screen = DefaultScreen(x_display);
visual_list = XGetVisualInfo(x_display, VisualScreenMask, &visual_template, &nxvisuals);
XMatchVisualInfo(x_display, XDefaultScreen(x_display), 32, TrueColor, &vinfo))
parent = XDefaultRootWindow(x_display);
XSync(x_display, True);
visual = vinfo.visual;
depth = vinfo.depth;
XSetWindowAttributes swa;
swa.event_mask = ExposureMask | PointerMotionMask | KeyPressMask;
swa.colormap = XCreateColormap(x_display, XDefaultRootWindow(x_display), visual, AllocNone);
swa.background_pixel = 0;
swa.border_pixel = 0;
main_window = XCreateWindow (
x_display, parent,
0, 0, m_plane_width, m_plane_height, 0,
depth, InputOutput,
visual, CWEventMask | CWBackPixel | CWColormap | CWBorderPixel,
&swa );
/* Create a window */
opengl_window = XCreateWindow (
x_display, main_window,
0, 0, m_plane_width, m_plane_height, 0,
depth, InputOutput,
visual, CWEventMask | CWBackPixel | CWColormap | CWBorderPixel,
&swa );
int cnxvisuals = 0;
XVisualInfo cvisual_template;
XVisualInfo *cvisual_list;
XVisualInfo cvinfo;
Visual *cvisual;
int cdepth;
cvisual_template.screen = DefaultScreen(x_display);
cvisual_list = XGetVisualInfo(x_display, VisualScreenMask, &cvisual_template, &cnxvisuals);
/* The gstreamer video sink must play on a screen with 24-bit color. Otherwise it will fail */
XMatchVisualInfo(x_display, XDefaultScreen(x_display), 24, TrueColor, &cvinfo)
XSync(x_display, True);
cvisual = cvinfo.visual;
cdepth = cvinfo.depth;
XSetWindowAttributes cswa;
cswa.event_mask = PointerMotionMask | KeyPressMask;
cswa.colormap = XCreateColormap(x_display, XDefaultRootWindow(x_display), cvisual, AllocNone);
cswa.background_pixel = 0;
cswa.border_pixel = 0;
/* Create the window */
gstreamer_window = XCreateWindow (
x_display, main_window,
0, 0, m_plane_width, m_plane_height, 0,
cdepth, InputOutput,
cvisual, CWEventMask | CWBackPixel | CWColormap | CWBorderPixel,
&cswa );
Window stack_position[2] = {opengl_window, gstreamer_window};
XRestackWindows(x_display, stack_position, 2);

Move window event on X11 window

I currently have 2 X11 windows that I want to keep in sync. One overlays a transparent graphic while the other one shows video. I currently have the one that overlays graphic to be always on top of the one of the video, but I am having trouble making sure both windows are at the same places when moved. I am looking for a window move event in the X11 documentation, but I can't seem to find one.
In addition, in my event handle loop, I have tried to get the location of one of my windows and to move the other window to that location. This has failed whenever XGetWindowAttributes is called it always returns x=0 and y=0 even though the window is moved.
Window win;
int nxvisuals = 0;
XVisualInfo visual_template;
XVisualInfo *visual_list;
XVisualInfo vinfo;
Visual *visual;
int depth;
Atom wm_state;
(void)wm_state;
x_display = XOpenDisplay ( NULL ); // open the standard display (the primary screen)
visual_template.screen = DefaultScreen(x_display);
visual_list = XGetVisualInfo(x_display, VisualScreenMask, &visual_template, &nxvisuals);
XMatchVisualInfo(x_display, XDefaultScreen(x_display), 32, TrueColor, &vinfo)
Window parent = XDefaultRootWindow(x_display);
XSync(x_display, True);
visual = vinfo.visual;
depth = vinfo.depth;
XSetWindowAttributes swa;
swa.event_mask = ExposureMask | PointerMotionMask | KeyPressMask;
swa.colormap = XCreateColormap(x_display, XDefaultRootWindow(x_display), visual, AllocNone);
swa.background_pixel = 0;
swa.border_pixel = 0;
win = XCreateWindow ( // create a window with the provided parameters
x_display, parent,
0, 0, 1024, 576, 0,
depth, InputOutput,
visual, CWEventMask | CWBackPixel | CWColormap | CWBorderPixel,
&swa );
XSync(x_display, True);
XSetWindowAttributes xattr;
xattr.override_redirect = False;
XChangeWindowAttributes ( x_display, win, CWOverrideRedirect, &xattr );
XWMHints hints;
hints.input = True;
hints.flags = InputHint;
XSetWMHints(x_display, win, &hints);
XSizeHints *size_hints = XAllocSizeHints();
size_hints->flags = PMinSize | PMaxSize | PSize;
size_hints->min_width = 1024;
size_hints->max_width = 1024;
size_hints->min_height = 576;
size_hints->max_height = 576;
XSetNormalHints(x_display, win, size_hints);
XSetWMSizeHints(x_display,win , size_hints, PSize | PMinSize | PMaxSize);
XMapWindow ( x_display , win ); // make the window visible on the screen
XStoreName ( x_display , win , "OpenGL" ); // give the window a name
/* Second window starts here */
int cnxvisuals = 0;
XVisualInfo cvisual_template;
XVisualInfo *cvisual_list;
XVisualInfo cvinfo;
Visual *cvisual;
int cdepth;
cvisual_template.screen = DefaultScreen(x_display);
cvisual_list = XGetVisualInfo(x_display, VisualScreenMask, &cvisual_template, &cnxvisuals);
XMatchVisualInfo(x_display, XDefaultScreen(x_display), 24, TrueColor, &cvinfo)
Window child = XDefaultRootWindow(x_display);
XSync(x_display, True);
cvisual = cvinfo.visual;
cdepth = cvinfo.depth;
XSetWindowAttributes cswa;
cswa.event_mask = PointerMotionMask | KeyPressMask;
cswa.colormap = XCreateColormap(x_display, XDefaultRootWindow(x_display), cvisual, AllocNone);
cswa.background_pixel = 0;
cswa.border_pixel = 0;
child = XCreateWindow ( // create a window with the provided parameters
x_display, parent,
0, 0, 1024, 576, 0,
cdepth, InputOutput,
cvisual, CWEventMask | CWBackPixel | CWColormap | CWBorderPixel,
&cswa );
XSync(x_display, True);
XSetWindowAttributes xcattr;
xcattr.override_redirect = False;
XChangeWindowAttributes ( x_display, child, CWOverrideRedirect, &xcattr );
XWMHints chints;
chints.input = True;
chints.flags = InputHint;
XSetWMHints(x_display, child, &chints);
XSetNormalHints(x_display, child, size_hints);
XSetWMSizeHints(x_display,child , size_hints, PSize | PMinSize | PMaxSize);
XMapWindow ( x_display , child ); // make the window visible on the screen
XStoreName ( x_display , child , "video" ); // give the window a name
XSelectInput(x_display, child, ExposureMask | FocusChangeMask);
int id = pthread_create(&x11loop, NULL,x11_handle_events,this);
Here is my handle events call
void* x11_handle_events(void *void_ptr)
{
Renderer* renderer = static_cast<Renderer*>(void_ptr);
renderer->stop = false;
XEvent event;
XWindowAttributes opengl_attrs;
while(!renderer->stop)
{
XNextEvent(renderer->x_display, &event);
switch(event.type)
{
case Expose:
if (event.xexpose.window == renderer->child)
{
XRaiseWindow(renderer->x_display, renderer->win);
}
break;
case FocusIn:
if (event.xfocus.window == renderer->child)
{
XRaiseWindow(renderer->x_display, renderer->win);
}
break;
}
// Make sure both windows are in the same location
XGetWindowAttributes(renderer->x_display, renderer->child, &opengl_attrs);
XMoveWindow(renderer->x_display, renderer->win, opengl_attrs.x, opengl_attrs.y);
}
pthread_exit(0);
return NULL;
}
The event you're looking for is ConfigureNotify
http://tronche.com/gui/x/xlib/events/window-state-change/configure.html
The X server can report ConfigureNotify events to clients wanting information about actual changes to a window's state, such as size, position, border, and stacking order. The X server generates this event type whenever one of the following configure window requests made by a client application actually completes:
snip
A window is moved by calling XMoveWindow().
The x and y members are set to the coordinates relative to the parent window's origin and indicate the position of the upper-left outside corner of the window. The width and height members are set to the inside size of the window, not including the border. The border_width member is set to the width of the window's border, in pixels.
The event mask is iirc StructureNotifyMask.
The window manager might disagree with your moving around though... but if it still doesn't work, leave a comment and we'll look deeper.

Render to Desktop

I want to be able to render a thing as if it was a wallpaper. I use Windows, and I prefer DirectX. I know that VLC can render the video has a wallpaper in DirectX mode, so it's possible.
So, a quick question, How could I set the rendertarget to render like if it was a wallpaper in Windows?
Here is some code which will get you a handle (HWND) to a window that can be used to draw over top of the windows Desktop. The main issue with how this works is that the desktop icons are still present but this will allow you to draw over top of them. If you want the icons to appear as normal (with your stuff behind them) you need to redraw them after you've drawn your stuff, or find a way to avoid drawing over them in the first place. This is fairly non-trivial and something I never completely solved.
This definitely works on XP and Windows 7 (with Areo) for getting something that normal GDI drawing can use. I've never tested it with DirectX but I suspect it would work if you used hMainWnd as your presentation window.
HWND hProgMan = NULL;
HWND hShell = NULL;
HWND hMainWnd = NULL;
unsigned int ScreenWidth = 0;
unsigned int ScreenHeight = 0;
int ScreenTop = 0;
int ScreenLeft = 0;
HRGN ValidRGN = NULL;
// ...
ScreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
if ( ScreenWidth == 0 )
ScreenWidth = GetSystemMetrics( SM_CXSCREEN );
ScreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
if ( ScreenHeight == 0 )
ScreenHeight = GetSystemMetrics(SM_CYSCREEN);
ScreenTop = GetSystemMetrics(SM_YVIRTUALSCREEN);
ScreenLeft = GetSystemMetrics(SM_XVIRTUALSCREEN);
ValidRGN = CreateRectRgn(0,0,ScreenWidth,ScreenHeight);
hProgMan = FindWindow("Progman", "Program Manager");
if(hProgMan != NULL)
{
hShell = FindWindowEx(hProgMan, 0, "SHELLDLL_DefView", NULL);
}
else
{
hProgMan = FindWindow("DesktopBackgroundClass", NULL);
if(hProgMan != NULL)
hShell = FindWindowEx(hProgMan, 0, "DeskFolder", NULL);
}
hMainWnd = CreateWindowEx( WS_EX_TRANSPARENT, "MyWindowClass", "Window Title", WS_CHILDWINDOW | WS_OVERLAPPED | WS_CLIPCHILDREN, 0,0,ScreenWidth,ScreenHeight, hShell,NULL,hInstance,NULL );
EnableWindow(hMainWnd,FALSE);
SetWindowPos(hMainWnd,HWND_BOTTOM,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE);
... and then for drawing (using GDI), something like this...
HDC hDC = GetDC( hMainWnd );
SelectClipRgn(hDC,ValidRGN);
BitBlt( hDC, 0, 0, ScreenX, ScreenY, hBackBuffer, 0, 0, SRCCOPY );
ReleaseDC( hMainWnd, hDC );
... and update ValidRGN with the regions of the Desktop icons. Those can be found with a bit of work with the Desktop's listview control window. That is fairly complicated and maybe off topic for this question.