Correct use of the opaque window object?

mariojmartin wrote on Thursday, October 10, 2013:

I am trying to understand how to use the opaque window handler. If I understand correctly, it is an incomplete struct. So I have made a simple code to experiment with, but something wrong is meshing with the memory.

This example opens three windows in a separated thread. It is working fine until the windows are closed and an unhandled error is triggered in glfwDestroyWindow().

Any suggestions?


#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <string.h>
#include <stdio.h>

#include "GLFW/glfw3.h"

#define MAX_NUMBER_WINDOWS 8
#define OPEN_WINDOW 1
#define CLOSE_WINDOW 2
#define FREEZE_WINDOW 3
#define TERMINATE 4
#define FRAMERATE_MILISECONDS 20

struct UserDefined
{
    int id;
};

struct WindowHandler
{
    GLFWwindow* window;
    int status;
};

WindowHandler window_handler[MAX_NUMBER_WINDOWS] = {{NULL, 0}};

/* GLFW error callback, when an error is reported */
static void error_callback( int error, const char* description )
{
    printf( "%s\n", description );
}

/* Callback that is triggered everytime the keyboard inputs */
static void callback_keyboard( GLFWwindow* window, int key, int scancode, int action, int mods )
{
    UserDefined* p = (UserDefined*)glfwGetWindowUserPointer( window );
    if (p != NULL){
        printf("[%i] %c\n", p->id, (char)key);
    }
}

static GLFWwindow* create_window( const int window_number )
{
    /* Create the render context */
    char title[32];
    sprintf( title, "Window %i", window_number );
    GLFWwindow* window = glfwCreateWindow(640, 640, title, NULL, NULL);
    UserDefined* p = new(UserDefined);
    p->id = window_number; 
    glfwSetWindowUserPointer( window, p );

    if ( window == NULL ){
        return NULL;
    }

    glfwMakeContextCurrent( window );

    glfwSetKeyCallback( window, callback_keyboard );

    return window;
}

static void ow_glfw_render_loop()
{
    std::mutex mutex;

    glfwSetErrorCallback(error_callback);

    if (glfwInit() == GL_FALSE){
        return;
    }

    while(1)
    {
        clock_t start_clock = clock();

        for (int slot = 0; slot < MAX_NUMBER_WINDOWS; slot++)
        {
            WindowHandler* handler = &(window_handler[slot]);

            if (handler->status == OPEN_WINDOW){
                /* Creates a new render context */ 
                mutex.lock();
                handler->window = create_window( slot );
                handler->status = 0;
                mutex.unlock();
            }

            if (handler->status == CLOSE_WINDOW)
            {
                /* Destroys the render context */
                mutex.lock();
                UserDefined* p = (UserDefined*)glfwGetWindowUserPointer( handler->window );
                if (p != NULL){
                    delete(p);
                }
                glfwDestroyWindow( handler->window );
                handler->window = NULL;
                handler->status = 0;
                mutex.unlock();
            }

            if (handler->window != NULL && handler->status == 0)
            {
                glfwMakeContextCurrent( handler->window );

                /* Display goes here */

                glfwSwapBuffers( handler->window );

                if (glfwWindowShouldClose( handler->window ) == GL_TRUE){
                    mutex.lock();
                    handler->status = CLOSE_WINDOW;
                    mutex.unlock();
                }
            }
        }

        glfwPollEvents();

        clock_t end_clock = clock();
        clock_t sleep_time = FRAMERATE_MILISECONDS 
            + ((start_clock - end_clock)* 1000)/CLOCKS_PER_SEC;

        if (sleep_time > 0){
            std::this_thread::sleep_for
                ( std::chrono::milliseconds( sleep_time ));
        }
    }

    glfwTerminate();
}


int main(int argc, char *argv[])
{
    /* Launch the main loop in a thread */
    std::thread render_thread_loop(ow_glfw_render_loop);
    render_thread_loop.detach();

    std::mutex mutex;
    mutex.lock();
    window_handler[0].status = OPEN_WINDOW;
    window_handler[1].status = OPEN_WINDOW;
    window_handler[2].status = OPEN_WINDOW;
    mutex.unlock();

    getchar();
}


elmindreda wrote on Thursday, October 10, 2013:

GLFWwindow is an opaque struct. You should not define it or change anything inside it. See the documentation for information on how to use the GLFW API.

mariojmartin wrote on Friday, October 11, 2013:

Thanks for the quick answer.

Ok, In this case, we are talking about different concepts. I thought it was something similar to this:

Anyway, the documentation does not suggest how to manage several windows. Maybe, the possibility to assign an external pointer to the window data structure would simplify it.

elmindreda wrote on Friday, October 11, 2013:

Yes, a GLFWwindow* is an opaque pointer.

http://www.glfw.org/docs/latest/news.html#news_30_wndptr

marcel_metz wrote on Friday, October 11, 2013:

Anyway, the documentation does not suggest how to manage several
windows. Maybe, the possibility to assign an external pointer to the
window data structure would simplify it.

You probably want to use the glfwGetWindowUserPointer [1] and
glfwSetWindowUserPointer [2] function pair to do that.

[1]
http://www.glfw.org/docs/latest/group__window.html#ga17807ce0f45ac3f8bb50d6dcc59a4e06
[2]
http://www.glfw.org/docs/latest/group__window.html#ga3d2fc6026e690ab31a13f78bc9fd3651

mariojmartin wrote on Friday, October 11, 2013:

Ok. I missed that part. It makes more sense now. I modified the example and now is working right.

Thanks very much.