Subversion Repositories seema-scanner

Rev

Rev 16 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include "OpenGLContext.h"

#include <GL/gl.h>
#include <GL/glx.h>
#include <X11/Xlib.h>
#include <X11/Xcursor/Xcursor.h>

#include <stdio.h>
#include <string.h>

#include <unistd.h>

// vmode extension for mode settings, gamma control, etc.
#include <X11/extensions/xf86vmode.h>

// XRandR extension for virtual screens, mode setting, etc.
#include <X11/extensions/Xrandr.h>

struct OpenGLContext::OpenGLContextInfo{
    Display *display;
    Window window;
    GLXContext context;
    OpenGLContextInfo() : display(NULL), window(0), context(NULL){}
};

std::vector<ScreenInfo> OpenGLContext::GetScreenInfo(){
    std::vector<ScreenInfo> ret;

    // Connection to default X Server
    Display *display = XOpenDisplay(NULL);

    RROutput primaryOutput = XRRGetOutputPrimary(display, RootWindow(display, 0));
    XRRScreenResources* sr = XRRGetScreenResources(display, RootWindow(display, 0));

    // number of virtual (XRandr screens)
    unsigned int nVirtualScreens = sr->ncrtc;

    for (unsigned int j = 0;  j < nVirtualScreens;  j++){
        XRROutputInfo* oi;
        XRRCrtcInfo* ci;
        RROutput output;

        // check if the crt has outputs
        ci = XRRGetCrtcInfo(display, sr, sr->crtcs[j]);
        if (ci->noutput == 0){
            XRRFreeCrtcInfo(ci);
            continue;
        }

        output = ci->outputs[0];

        // if primaryoutput is among outputs, use that instead of the first
        for (int k = 0;  k < ci->noutput;  k++){
            if (ci->outputs[k] == primaryOutput){
                output = primaryOutput;
                break;
            }
        }

        // check that the output is connected
        oi = XRRGetOutputInfo(display, sr, output);
        if(oi->connection != RR_Connected){
            XRRFreeOutputInfo(oi);
            XRRFreeCrtcInfo(ci);
            continue;
        }

        ScreenInfo info;
        info.name = oi->name;

        // get current mode
        XRRModeInfo *modeInfo;
        for (int k = 0;  k < sr->nmode;  k++){
            if (sr->modes[k].id == ci->mode){
                modeInfo = sr->modes + k;
                break;
            }
        }

        info.resX = modeInfo->width;
        info.resY = modeInfo->height;

        info.posX = ci->x;
        info.posY = ci->y;

        ret.push_back(info);
    }
    XRRFreeScreenResources(sr);

    XCloseDisplay(display);

    return ret;
}


OpenGLContext::OpenGLContext(uint _screenNum) : screenNum(_screenNum){

    std::vector<ScreenInfo> virtualScreens = GetScreenInfo();
    if(screenNum > virtualScreens.size())
        std::cerr << "OpenGLContext: cannot open screen no. " << screenNum << "!" << std::endl;

    ScreenInfo screen = virtualScreens[screenNum];
    screenResX = screen.resX;
    screenResY = screen.resY;

    contextInfo = new OpenGLContextInfo();
    contextInfo->display = XOpenDisplay(NULL);

    int attrListDbl[] = {
        GLX_RGBA, GLX_DOUBLEBUFFER,
        GLX_RED_SIZE, 4,
        GLX_GREEN_SIZE, 4,
        GLX_BLUE_SIZE, 4,
        None
    };

    XVisualInfo *visualInfo = glXChooseVisual(contextInfo->display, 0, attrListDbl);

    int glxMajor, glxMinor = 0;
    glXQueryVersion(contextInfo->display, &glxMajor, &glxMinor);
    std::cout << "GLX-Version " << glxMajor << "." << glxMinor << std::endl;

    // Create a GLX OpenGLContext
    bool directRendering = True;
    contextInfo->context = glXCreateContext(contextInfo->display, visualInfo,  NULL, directRendering);

    // Check if OpenGLContext is direct
    if (glXIsDirect(contextInfo->display, contextInfo->context))
        std::cout << "OpenGLContext is direct." << std::endl;
    else
        std::cout << "OpenGLContext is not direct." << std::endl;

    // Create colormap
    Colormap colormap = XCreateColormap(contextInfo->display, RootWindow(contextInfo->display, 0), visualInfo->visual, AllocNone);

    // Create the actual window
    unsigned long wamask = CWColormap;

    XSetWindowAttributes wa;
    wa.colormap = colormap;
    wa.border_pixel = 0;
    wa.event_mask = 0;

    // bypass window manager (has no effect here)
    wa.override_redirect = True;

    // show no cursor
    wa.cursor = None;

    contextInfo->window = XCreateWindow(contextInfo->display, RootWindow(contextInfo->display, 0), screen.posX, screen.posY, screenResX, screenResY, 0, visualInfo->depth, InputOutput, visualInfo->visual, wamask, &wa);

    // move to correct position (necessary when not overriding redirect)
    XMoveWindow(contextInfo->display, contextInfo->window, screen.posX, screen.posY);

    if(!contextInfo->window)
        std::cerr << "Failed to create X window!" << std::endl;

    // bypass window manager (actually effects here)
    XSetWindowAttributes attributes;
    attributes.override_redirect = False;
    XChangeWindowAttributes(contextInfo->display, contextInfo->window, CWOverrideRedirect, &attributes);

    // Disable mouse cursor over window
    XcursorImage *cursorImage = XcursorImageCreate(1, 1);
    *cursorImage->pixels = 0;
    cursorImage->xhot = 0;
    cursorImage->yhot = 0;
    Cursor cursor = XcursorImageLoadCursor(contextInfo->display, cursorImage);
    XDefineCursor(contextInfo->display, contextInfo->window, cursor);
    XFreeCursor(contextInfo->display, cursor);

    // Connect the glx-OpenGLContext to the window
    glXMakeCurrent(contextInfo->display, contextInfo->window, contextInfo->context);

    // Window state
    Atom wmNetWmState = XInternAtom(contextInfo->display, "_NET_WM_STATE", 1);
    // raise window above most other windows
    Atom wmStateAbove = XInternAtom(contextInfo->display, "_NET_WM_STATE_ABOVE", 1);
    // define window as fullscreen
    Atom wmStateFullScreen = XInternAtom(contextInfo->display, "_NET_WM_STATE_FULLSCREEN", 1);

    XEvent event;
    memset(&event, 0, sizeof(event));

    event.type = ClientMessage;
    event.xclient.window = contextInfo->window;
    event.xclient.format = 32; // Data is 32-bit longs
    event.xclient.message_type = wmNetWmState;
    event.xclient.data.l[0] = 1; // Add the state
    event.xclient.data.l[2] = 0; // No secondary property
    event.xclient.data.l[3] = 1; // Sender is a normal application

    event.xclient.data.l[1] = wmStateAbove;
    XSendEvent(contextInfo->display, RootWindow(contextInfo->display, 0), False, SubstructureNotifyMask | SubstructureRedirectMask, &event);

    event.xclient.data.l[1] = wmStateFullScreen;
    XSendEvent(contextInfo->display, RootWindow(contextInfo->display, 0), False, SubstructureNotifyMask | SubstructureRedirectMask, &event);

//    // Set active window
//    Atom wmActiveWindow = XInternAtom(contextInfo->display, "_NET_WM_ACTIVE_WINDOW", 1);

//    event.xclient.message_type = wmActiveWindow;
//    event.xclient.data.l[0] = 1; // Sender is a normal application
//    event.xclient.data.l[1] = 0; // We don't really know the timestamp

//    XSendEvent(contextInfo->display, RootWindow(contextInfo->display, 0), False, SubstructureNotifyMask | SubstructureRedirectMask, &event);


    // Set swap interval to 1 for vsync
    const char *glx_extensions = glXQueryExtensionsString(contextInfo->display, 0);
    if (strstr(glx_extensions, "GLX_SGI_swap_control")) {
        PFNGLXSWAPINTERVALSGIPROC SwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC)glXGetProcAddressARB((const GLubyte*)"glXSwapIntervalSGI");
        SwapIntervalSGI(1);
    } else if (strstr(glx_extensions, "GLX_EXT_swap_control")) {
        PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddressARB((const GLubyte*)"glXSwapIntervalEXT");
        SwapIntervalEXT(contextInfo->display, contextInfo->window, 1);
    } else {
        std::cerr << "OpenGLContext Error: Could not access swap interval extension!" << std::endl;
    }

    // Raise window (necessary)
    XMapRaised(contextInfo->display, contextInfo->window);

    XFlush(contextInfo->display);

    // Adjust gamma to one
//    setGamma(1.9);
}

void OpenGLContext::setGamma(float gamma){
    // Adjust gamma
    XF86VidModeGamma xf86Gamma = {gamma, gamma, gamma};
    XF86VidModeSetGamma(contextInfo->display, screenNum, &xf86Gamma);
}

void OpenGLContext::makeContextCurrent(){
    glXMakeCurrent(contextInfo->display, contextInfo->window, contextInfo->context);
}

void OpenGLContext::flush(){

    // Swap buffers
    glXSwapBuffers(contextInfo->display, contextInfo->window);

    // Synchronize CPU with vsync buffer swap
    //glFinish();
    //glXWaitGL();
}

OpenGLContext::~OpenGLContext(){
    std::cout<<"Releasing OpenGL Context\n"<<std::flush;
    if(contextInfo->context){
        // Release context (None, NULL)
        if(!glXMakeCurrent(contextInfo->display, None, NULL))
            std::cerr << "Error. Could not release drawing OpenGLContext." << std::endl;

        glXDestroyContext(contextInfo->display, contextInfo->context);
        XCloseDisplay(contextInfo->display);
        delete contextInfo;
    }
}