on Windows you can draw on top of everything with GDI, taking the draw context of null:
HDC hdc = GetDC(NULL);
I whish to do the same with SFML, but if i try something equivalent (creating render window with NULL as argument, after casting it to hwnd) nothing is drawn anywhere. Is what i'm trying even possible with sfml?
well if you want OpenGL you need a window period. But the window does not need to be visible on the screen. You can combine GDI and OpenGL together to achieve your goal.
Render stuff off screen into bitmap by OpenGL
using invisible window with the same resolution as your desktop. If the window is invisible it will not react to mouse or keyboard events ...
Copy GL image into CPU side memory
simple glReadPixels
will do.
Copy the image into Desktop (using GDI Bitmap)
simply convert/copy the raw image data into GDI compatible bitmap and then simply draw it onto desktop canvas. So no SwapBuffers(hdc);
as in standard GL app anymore.
I am coding in C++/VCL environment so I do not have pure WinAPI/GDI knwoledge (VCL do it for me but the code should be very similar the names and operands passed might vary a bit but not too much).
This is what I come with:
//---------------------------------------------------------------------------
#include <vcl.h>
#include <gl\gl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1; // VCL Application window object
TCanvas *scr=NULL; // Desktop
DWORD *txr=NULL; // memory for GPU->CPU image transfer
Graphics::TBitmap *bmp=NULL; // bitmap for CPU->Desktop image transfer
int xs,ys; // desktop resolution
HDC hdc=NULL; // device context for GL
HGLRC hrc=NULL; // rendering context for GL
//---------------------------------------------------------------------------
void gl_draw()
{
if (scr==NULL) return;
if (bmp==NULL) return;
if (txr==NULL) return;
glClearColor(0.0,0.0,0.0,0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
// desktop pixel units
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glTranslatef(-1.0,+1.0,0.0);
glScalef(2.0/float(xs),-2.0/float(ys),1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// render rectangle
GLfloat fx=xs/2,fy=ys/2,fz=0.0,fa=(xs/2)-10,fb=(ys/2)-10;
glColor3f(1.0,1.0,1.0);
glBegin(GL_LINE_LOOP);
glVertex3f(fx-fa,fy-fb,fz);
glVertex3f(fx-fa,fy+fb,fz);
glVertex3f(fx+fa,fy+fb,fz);
glVertex3f(fx+fa,fy-fb,fz);
glEnd();
if (Form1->Visible) // normal window GL render
{
glFlush();
SwapBuffers(hdc);
}
else{ // copy GL image directly to desktop
// copy GL image to CPU side memory
glFlush();
glReadPixels(0,0,xs,ys,GL_RGBA,GL_UNSIGNED_BYTE,txr);
// copy it to bitmap
int x,y,a; DWORD *p;
for (a=0,y=0;y<ys;y++)
for (p=(DWORD*)bmp->ScanLine[y],x=0;x<xs;x++,a++)
p[x]=txr[a];
// render it to desktop
scr->Draw(0,0,bmp);
}
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
{
// desktop
scr=new TCanvas();
scr->Handle=GetDC(NULL);
xs=scr->ClipRect.Width();
ys=scr->ClipRect.Height()-31; // leave taskbar out of it
// BMP
bmp=new Graphics::TBitmap;
bmp->HandleType=bmDIB;
bmp->PixelFormat=pf32bit;
bmp->SetSize(xs,ys);
// txr buffer
txr=new DWORD[xs*ys];
// window
BorderStyle=bsNone;
SetBounds(0,0,xs,ys);
// GL init
hdc = GetDC(Handle); // get device context for this App window
PIXELFORMATDESCRIPTOR pfd;
ZeroMemory( &pfd, sizeof( pfd ) ); // set the pixel format for the DC
pfd.nSize = sizeof( pfd );
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
pfd.cDepthBits = 24;
pfd.iLayerType = PFD_MAIN_PLANE;
SetPixelFormat(hdc,ChoosePixelFormat(hdc, &pfd),&pfd);
hrc = wglCreateContext(hdc); // create current rendering context
if(hrc == NULL)
{
ShowMessage("Could not initialize OpenGL Rendering context !!!");
Application->Terminate();
}
if(wglMakeCurrent(hdc, hrc) == false)
{
ShowMessage("Could not make current OpenGL Rendering context !!!");
wglDeleteContext(hrc); // destroy rendering context
Application->Terminate();
}
glViewport(0,0,xs,ys);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
// GL exit
wglMakeCurrent(NULL, NULL); // release current rendering context
wglDeleteContext(hrc); // destroy rendering context
// release buffers
if (scr){ delete scr; scr=NULL; }
if (bmp){ delete bmp; bmp=NULL; }
if (txr){ delete[] txr; txr=NULL; }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
gl_draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
if (Visible) Visible=false; // hide
gl_draw();
}
//---------------------------------------------------------------------------
Its single Form VCL application with single timer on it. It creates GL context and on first opportunity become invisible. Then it periodically overwrites desktop with black background and white rectangle border ...
So you need to port the VCL stuff (Form1
and events) into your environment. Also you might want to add transparency to the line:
scr->Draw(0,0,bmp);
Or read the desktop image and use it as a background texture for the GL rendering.
PS. pure GDI rendering is much simpler and probably faster than this but your have required GL so ...