Call OpenGL functions outside of the GtkGLArea signals - c

I am trying to create a library where it's possible to create a OpenGL context with GTK3 which gets rendered automatically or with 1 function call (something similar to a swap-buffers function). I was looking at the source code of GtkGLArea, which seems to have the following order of execution:
A new GtkGLArea is created with the superclass GtkWidget.
When the realize signal of the superclass is captured, a GdkGLContext
is created and a realize signal is given to the user. The user is supposed to put the initial OpenGL functions in a function passed to this signal.
The draw signal of the superclass is also captured, some OpenGL initialization code is called for binding the framebuffer (or texture) and the renderbuffer. The buffers are created if they don't exist. After this the render signal is given where the user is supposed to put the rendering OpenGL code. Finally the gdk_cairo_draw_from_gl function is called to draw the renderbuffer (or texture) on the Cairo context of the superclass passed through the draw signal.
For the user it basically comes down to this:
void realize(GtkGLarea *area){
// OpenGL initialization functions
}
gboolean render(GtkGLArea *area, GdkGLContext *context){
// OpenGL rendering functions
return TRUE;
}
// Init functions, setup a GTK window
GtkWidget *gl_area = gtk_gl_area_new();
g_signal_connect(gl_area, "realize", G_CALLBACK(realize), NULL);
g_signal_connect(gl_area, "render", G_CALLBACK(render), NULL);
// Add gl_area to the window and display the window
But that's not what I want, what I want is this:
// Init functions, setup a GTK window
// Setup a OpenGL context on the GTK window
// OpenGL initialization functions
while(1){ // Main loop
if(gtk_events_pending()){
gtk_main_iteration();
}
// OpenGL rendering functions
swapBuffers();
}
What would be the best way to achieve this? I tried this (unsuccessfully) by simulating the functions called around the signals, and by using a GdkGLContext created on a GdkWindow taken from a GtkDrawingArea.
These are the options which I can imagine as solutions now:
Create a custom GTK3 class extending GtkWidget and using GdkGLContext and somehow track when the drawing signal is called so the appropriate OpenGL functions are called around that signal.
Find a way to ignore the whole GTK3 part and try to use the underlying Xlib functions to create a OpenGL context through X11 (which isn't portable at all).
Use a evil hack with threads and setjmp and longjmp to enter and exit the signals from the swapBuffers function.
None of these "solutions" are really appealing to me, is the thing I want impossible, or am I just missing some information?

Related

Multithreaded object loading while rendering with OpenGL

I want to load some textures and meshes in a separate thread while the main program is showing a loading screen because it takes a few seconds to load all resources. I'm using OpenGL and GLFW. I tried to accomplish this with the following code:
void *status;
if(pthread_create(&loader, NULL, &loader_func, NULL))
{
fprintf(stderr, "Error creating loader thread\n");
return 1;
}
while(_flags & FLAG_LOADING)
{
vec3 color = { 0.1, 0.3, 1.0 };
if(glfwWindowShouldClose(window))
{
resource_destroy();
glfwTerminate();
return 0;
}
GL_CHECK(glClearColor(0.1, 0.1, 0.1, 1.0));
GL_CHECK(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
font_renderer_activate();
render_string(&_font_menu, "Loading...", _width / 2, _height / 2, 64,
color, ALIGN_V_CENTER | ALIGN_H_CENTER);
glfwSwapBuffers(window);
glfwPollEvents();
}
if(pthread_join(loader, &status))
{
fprintf(stderr, "Error joining loader and main thread\n");
return 1;
}
if(*(int *)status)
{
fprintf(stderr, "Error loading resources\n");
return 1;
}
loader_func() is not rendering to the screen, and only uses OpenGL functions for creating VAOs, VBOs etc. and loading data into them.
The problem is that after the loading text shows up on screen and loading has finished, nothing shows up on screen (EDIT: except the textual HUD) and I'm getting a lot of debug error messages in my log (I'm wrapping all OpenGL calls in a macro that checks for errors with glGetError):
main.c:588
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap_texture);
GL_Error 0x502: GL_INVALID_OPERATION
main.c:589
glDrawArrays(GL_TRIANGLES, OFFSET_SKYBOX, VERTICES_SKYBOX);
GL_Error 0x502: GL_INVALID_OPERATION
main.c:629
glDrawArrays(GL_TRIANGLES, OFFSET_SELECTOR, VERTICES_SELECTOR);
GL_Error 0x502: GL_INVALID_OPERATION
When I call loader_func directly, there are no errors and the main render loop works correctly.
I read that to use OpenGL functions in another thread it is required to call glfwMakeContextCurrent but that wouldn't work in my case, because then the loading screen wouldn't be rendered. My only idea was to utilize a second library like SDL to create a window while loading, then destroy it and create a new window with GLFW for use with OpenGL. Is that what I want to achieve possible with just OpenGL?
The easiest way to handle this is to have the main thread create and manage all of the OpenGL objects, while the loading thread does the File IO (easily the slowest part of the loading). Once the loading thread is finished with loading a particular asset, it can deliver the loaded data to the main thread via <insert your favorite thread-safe mechanism here>, which can do the final OpenGL uploading part.
After all, it's not like rendering a loading screen is a huge performance drain or something, so the cost of uploading on the main thread will be minimal. This also permits you to do that loading bar thing, since your main thread will frequently be getting the results of the loading process, so it knows at any time how much of the loading is finished.
If you absolutely must have two threads both making OpenGL calls for some reason, then you should also have two OpenGL contexts, each being current in a different thread, and the two contexts sharing objects with each other. GLFW is perfectly happy to provide this if you ask it nicely. Create your main window as normal, then set the GLFW_VISIBLE hint to GLFW_FALSE, and create a second window (with an arbitrary resolution). You should pass the main window as the last parameter to glfwCreateWindow, so that the two contexts can share objects. You can then set each window current in different contexts and you're fine.
One word of caution. Contexts that share objects between them only share certain objects. Objects which reference other objects cannot be shared (also query objects are unsharable for some reason). VAOs reference buffer objects, so they can't be shared. So there's no point in trying to create them on the off-screen context.

Gtk and C - Multi-threaded GUI Application and Removing Global Variables

I have the example GTK C application from [1] building and working as expected. I have a pretty little UI application with a + and - button to increment/decrement a value stored in a global variable, and render it in the application in a text label.
I rarely ever work with GUI applications, and I do 99% of my work in C. I have two key questions with respect to tidying up this example and using it as the basis of a project.
Is it possible to have some alternative to global variables, like a
custom struct I create in main(), and have every callback handler reference
it by changing the function protocol for increase()?
Code:
// Can this function protocol be modified?
void increase(GtkWidget *widget, gpointer label) {
count++;
sprintf(buf, "%d", count);
gtk_label_set_text(GTK_LABEL(label), buf);
}
g_signal_connect(minus, "clicked", G_CALLBACK(decrease), label);
Is there a simple means of creating a separate thread to help manage the GUI? For example, if I have a button tied/connected to a function that would take a minute to complete, is there a universally-accepted means of firing off a separate pthread that allows me to have a button or command to cancel the operation, rather than the entire UI app being blocked for the whole minute?
Thank you.
References
Cross Compiling GTK applications For the Raspberry Pi, Accessed 2014-02-20, <http://hertaville.com/2013/07/19/cross-compiling-gtk-applications-for-the-raspberry-pi/>
Yes, you can pass anything you like as the last argument to signal handlers (gpointer is a typedef for void*) just create the structure containing the label widget and the counter variable in main(), pass it as the last argument to g_signal_connect and cast it back to the proper type in your callback.
For running a calculation in another thread and delivering the result to the gtk main loop I'd look at GTask, in particular g_task_run_in_thread_async.

GTK+3 multithreading

I have a program (written in C, currently running on Mac OS X 10.8.4) that simulates a world and updates the world's state multiple times per second.
At startup this program creates a background thread which creates a simple GTK window and calls gtk_main.
After each time the world's state is updated (in the main thread), I would like the window to reflect that change. My first approach was to simply update the GTK widgets after the world was updated, but because that was in a different thread things broke quite messily.
Is there some sort of mechanism where the main thread can update some state on the graphics thread and then queue an event that prompts the graphics thread to redraw? I.e.
void draw() {
// This can only be called from the graphics thread.
gtk_label_set(GTK_LABEL(label1), "some state");
}
// This causes draw() to be called on the graphics thread.
gtk_please_redraw_this_thing_on_the_graphics_thread();
Is there any way to do this? Or any tutorials that cover it?
Turns out this is quite simple.
First create the function that will do the drawing (on the graphics thread):
gboolean draw_function(GtkWidget *w, GdkEventExpose *event) {
// Draw things.
return TRUE;
}
Then, during set up, connect the draw event of your widget to your draw function:
g_signal_connect(G_OBJECT(some_widget), "draw", G_CALLBACK(draw_function), NULL);
Finally, to force the widget to redraw from another thread you can call:
gtk_widget_queue_draw(some_widget);

How to insert synthetic mouse events into X11 input queue

I've got an embedded device running Linux/X11 that is connected to a device that provides touch events over a USB connection. This device is not recognized as any form of standard pointer/mouse input. What I'm trying to do is find a way to "inject" mouse events into X11 when the external device reports an event.
Doing so would remove the need for my application ( written in C using Gtk+ ) to fake mouse presses with Gtk+ calls.
If this can be done my Gtk+ application would not need to know or care about the device generating the touch events. It would just appear to the application as standard mouse events.
Anybody know how to go about inserting synthetic mouse events into X11?
Right now I'm doing the following which works, but isn't optimal.
GtkWidget *btnSpin; /* sample button */
gboolean buttonPress_cb( void *btn );
gboolean buttonDePress_cb( void *btn );
/* make this call after the device library calls the TouchEvent_cb() callback
and the application has determined which, if any, button was touched
In this example we are assuming btnSpin was touched.
This function will, in 5ms, begin the process of causing the button to do it's
normal animation ( button in, button out effects ) and then send the actual
button_clicked event to the button.
*/
g_timeout_add(5, (GSourceFunc) buttonPress_cb, (void *)btnSpin);
/* this callback is fired 5ms after the g_timeout_add() function above.
It first sets the button state to ACTIVE to begin the animation cycle (pressed look)
And then 250ms later calls buttonDePress_cb which will make the button look un-pressed
and then send the button_clicked event.
*/
gboolean buttonPress_cb( void *btn )
{
gtk_widget_set_state((GtkWidget *)btn, GTK_STATE_ACTIVE);
g_timeout_add(250, (GSourceFunc) buttonDePress_cb, btn);
return( FALSE );
}
/* Sets button state back to NORMAL ( not pressed look )
and sends the button_clicked event so that the registered signal handler for the
button can be activated
*/
gboolean buttonDePress_cb( void *btn )
{
gtk_widget_set_state( btn, GTK_STATE_NORMAL);
gtk_button_clicked( GTK_BUTTON( btn ));
return( FALSE );
}
The Linux input system has a facility for user-space implementation of input devices called uinput. You can write a background program that uses your device's callback library to send input events to the kernel. The X server (assuming it is using the evdev input module) would then process these just as any other mouse event.
There's a library called libsuinput that makes this fairly easy to do. It even includes an example mouse input program that you can probably use as a model. However, since your device is a touch-based device it will probably use absolute axes (ABS_X, ABS_Y) instead of relative (REL_X, REL_Y).
There are several methods.
Use XSendEvent. Caveat: some application frameworks ignore events sent with XSendEvent. I think Gtk+ doesn't, but I have not checked.
Use XTestFakeMotionEvent and XTestFakeButtonEvent. You need XTest extension on your X server.
Write a kernel driver for your device so that it will appear as a mouse/touchpad.
The coolest thing would be to implement a device driver inside the Kernel that creates a /dev/input/eventX file which speaks the evdev protocol. I recommend you to read the book called Linux Device Drivers if you want to do this. The book is freely available on the web.
If you want to do this in user space, I suggest you to use Xlib (or XCB). On plain Xlib (C language), you can use the X Test Extension or XSendEvent().
There's also a binary called xte from the xautomation package (on Debian, sudo apt-get install xautomation and then man xte). xte is very easy to use, and you can also look at its source code to learn how to use the X Test Extension.
Pointers:
http://lwn.net/Kernel/LDD3/
http://cgit.freedesktop.org/xorg/lib/libXtst/
http://cgit.freedesktop.org/xcb/xpyb/
http://hoopajoo.net/projects/xautomation.html
http://linux.die.net/man/1/xte
Seems that after a bit more research, Gtk+ uses a GDK library that can do what I want without having to delve deeply into X11 coding or write a Kernel driver. Although, if I had the time, I would prefer to write a Linux Kernel Mouse driver.
Using the GDK 2 Reference Manual I found I can do the following:
Use gtk_event_put() to append a GdkEvent of type GdkEventButton
The structure for a GdkEventButton is:
struct GdkEventButton {
GdkEventType type;
GdkWindow *window;
gint8 send_event;
guint32 time;
gdouble x;
gdouble y;
gdouble *axes;
guint state;
guint button;
GdkDevice *device;
gdouble x_root, y_root;
};
Most of these fields will be trivial to fill in with the exception of:
gdouble x;
the x coordinate of the pointer relative to the window.
gdouble y;
the y coordinate of the pointer relative to the window.
GdkDevice *device;
the device where the event originated.
gdouble x_root;
the x coordinate of the pointer relative to the root of the screen.
gdouble y_root;
the y coordinate of the pointer relative to the root of the screen.
I will need to research how to convert the screen root coordinates the window relative coordinates.
*device - I'm not sure if I need to use this field ( set to NULL ) because this is for an extended input device. However, if I do need to have a valid device here I should be able to use gdk_devices_list()

Embedding SDL into GTK+

I have an application that uses GTK+ to display some nice GUI, but I am using SDL to display a small RGB frame buffer inside GTK+
I have used the following code to get SDL into GTK+:
char SDL_windowhack[32];
sprintf(SDL_windowhack, "SDL_WINDOWID=%ld", GDK_WINDOW_XWINDOW(deviceWindow->window));
putenv(SDL_windowhack);
Unfortunately, I also use SDL for keyboard and mouse event. The main thread that uses SDL to update the image spawns the following thread:
void *SDLEvent(void *arg)
{
SDL_Event event;
while (1) {
fprintf(stderr, "Test\n");
SDL_WaitEvent(&event);
switch (event.type) {
/* ... */
}
}
}
I see that the print statement is executed twice, then none. As soon as I terminate the thread that SDL uses to update the screen (display), the loop in SDLEvent starts executing very fast again.
This code used to work fine before I integrated SDL into GTK+ so I am thinking GTK+ is maybe blocking SDL in some ways?
Does anyone have any suggestions please?
Thank you very much!
Although I have not used SDL, but as you are looking for events it appears that you are running two event loops. Gtk runs its own event loop which handles events like the ones from mouse & keyboard. I think you need to find a way to integrate the both. Some googling resulted in the following link where in the section "Double event loop issue" your problem has been addressed (I think). Try adding SDLEvent function as idler function using g_idle_add as suggested in the link and see if it works.
Hope this helps!

Resources