1

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?

Barnack
  • 921
  • 1
  • 8
  • 20
  • 1
    You cannot do that with SFML. – Jesper Juhl May 16 '19 at 15:53
  • sad and quick answer, ty @JesperJuhl – Barnack May 16 '19 at 15:57
  • According to the documentation you can create an `SFML` window from a native window handle `HWND` (https://www.sfml-dev.org/documentation/2.5.1/classsf_1_1Window.php & https://www.sfml-dev.org/documentation/2.5.1/group__window.php#gaed947028b0698a812cad2f97bfe9caa3) so something like `auto root_window = sf::Window(NULL, ...);` might work but I don't have Windows so I can't test that. – Galik May 16 '19 at 16:29
  • @Galik i tried creating an sf::Window with null as argument, even casting it to hwnd (sf::RenderWindow((HWND)NULL, ...)), it didn't work. No errors give but didn't even draw anywhere – Barnack May 17 '19 at 00:02
  • You can use full screen and use desktop as background texture ... or use transparent window ... as a workaround – Spektre May 17 '19 at 09:20
  • @Spektre such a workaround would still require a window, and it will get focus on mouse click and capture mouse and keyboard inputs, preventing anything else from getting any form of input except async device statuses. – Barnack May 17 '19 at 11:28
  • 1
    @Barnack well if you want OpenGL you need a window period .... but you can combine GDI and OpenGl so render stuff off screen into bitmap by OpenGL (and invisible window) and copy it to desktop Canvas using GDI. Also I am not fluent in WinAPI but may be there is also possible to tell Windows to ignore mouse clicks, key hits and focus events for the workaround window (like disabling mouse hooks etc)... by disabling the window ... – Spektre May 17 '19 at 11:52
  • i'll check that @Spektre, ty for the ideas – Barnack May 17 '19 at 12:17
  • @Spektre you can elaborate a bit more and post as an answer the whole "you can combine GDI and OpenGl so render stuff off screen into bitmap by OpenGL (and invisible window) and copy it to desktop Canvas using GDI" thing, i'll accept it – Barnack May 19 '19 at 16:18
  • 1
    @Barnack did try to implement it first ... looks like its working I added answer with simple example (far from perfect) – Spektre May 19 '19 at 19:44

1 Answers1

2

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.

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

  2. Copy GL image into CPU side memory

    simple glReadPixels will do.

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

Community
  • 1
  • 1
Spektre
  • 49,595
  • 11
  • 110
  • 380